Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.i18n / src / main / java / org / gvsig / i18n / Messages.java @ 41390

History | View | Annotate | Download (29.4 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24

    
25
package org.gvsig.i18n;
26

    
27
import java.io.File;
28
import java.io.IOException;
29
import java.io.InputStream;
30
import java.net.MalformedURLException;
31
import java.net.URL;
32
import java.text.MessageFormat;
33
import java.util.ArrayList;
34
import java.util.Enumeration;
35
import java.util.HashSet;
36
import java.util.IllegalFormatException;
37
import java.util.Iterator;
38
import java.util.List;
39
import java.util.Locale;
40
import java.util.Properties;
41
import java.util.Set;
42

    
43
import org.slf4j.Logger;
44
import org.slf4j.LoggerFactory;
45

    
46
/**
47
 * <p>This class offers some methods to provide internationalization services
48
 * to other projects. All the methods are static.</p>
49
 *
50
 * <p>The most useful method is {@link #getText(String) getText(key)} (and family),
51
 * which returns the translation associated
52
 * with the provided key. The class must be initialized before getText can be
53
 * used.</p>
54
 *
55
 * <p>The typical usage sequence would be:</p>
56
 * <ul>
57
 * <li>Add some locale to the prefered locales list: <code>Messages.addLocale(new Locale("es"))</code></li>
58
 * <li>Add some resource file containing translations: <code>Messages.addResourceFamily("text", new File("."))</code></li>
59
 * <li>And finaly getText can be used: <code>Messages.getText("aceptar")</code></li>
60
 * </ul>
61
 *
62
 * <p>The resource files are Java properties files, which contain <code>key=translation</code>
63
 * pairs, one
64
 * pair per line. These files must be encoded using iso-8859-1 encoding, and unicode escaped
65
 * sequences must be used to include characters outside the former encoding.
66
 * There are several ways to specify the property file to load, see the different
67
 * addResourceFamily methods for details.</p>
68
 *
69
 * @author Cesar Martinez Izquierdo (cesar.martinez@iver.es)
70
 *
71
 */
72
public class Messages {
73
    
74
    private static class FamilyDescriptor {
75
        String family = null;
76
        ClassLoader loader = null;
77
        String callerName = null;
78
        
79
        public FamilyDescriptor(String family, ClassLoader loader, String callerName ) {
80
            this.family  = family;
81
            this.loader = loader;
82
            this.callerName = callerName;
83
        }
84
    }
85
    
86
    private static Logger logger = LoggerFactory.getLogger("Messages");
87
    private static String _CLASSNAME = "org.gvsig.i18n.Messages";
88
    private static Locale currentLocale;
89

    
90
    /* Each entry will contain a hashmap with translations. Each hasmap
91
     * contains the translations for one language, indexed by the
92
     * translation key. The translations for language (i) in the preferred locales
93
     * list are contained in the position (i) of the localeResources list */
94
    private static ArrayList localeResources = new ArrayList();
95
    private static ArrayList preferredLocales = new ArrayList(); // contains the ordered list of prefered languages/locales (class Locale)
96
    private static Set notTranslatedKeys = new HashSet();
97

    
98

    
99
        /* Set of resource families and classloaders used to load i18n resources. */
100
        private static Set resourceFamilies = new HashSet();
101
        private static Set classLoaders = new HashSet();
102

    
103
        private static List familyDescriptors = new ArrayList();
104
        
105
        /*
106
         * The language considered the origin of translations, which will
107
         * (possibly) be stored in a property file without language suffix
108
         * (ie: text.properties instead of text_es.properties).
109
         */
110
        private static String baseLanguage = "es";
111
        private static Locale baseLocale = new Locale(baseLanguage);
112

    
113
        /**
114
         * <p>Gets the localized message associated with the provided key.
115
         * If the key is not in the dictionary, return the key and register
116
         * the failure in the log.</p>
117
         *
118
         * <p>The <code>callerName</code> parameter is only
119
         * used as a label when logging, so any String can be used. However, a
120
         * meaningful String should be used, such as the name of the class requiring
121
         * the translation services, in order to identify the source of the failure
122
         * in the log.</p>
123
         *
124
         * @param key         An String which identifies the translation that we want to get.
125
         * @param callerName  A symbolic name given to the caller of this method, to
126
         *                    show it in the log if the key was not found
127
         * @return            an String with the message associated with the provided key.
128
         *                    If the key is not in the dictionary, return the key. If the key
129
         *                    is null, return null.
130
         */
131
        public static String getText(String key, String callerName) {
132
                if (key==null) {
133
                        return null;
134
                }
135
                for (int numLocale=0; numLocale<localeResources.size(); numLocale++) {
136
                        // try to get the translation for any of the languagues in the preferred languages list
137
                        String translation = ((Properties)localeResources.get(numLocale)).getProperty(key);
138
                        if (translation!=null && !translation.equals("")) {
139
                                return translation;
140
                        }
141
                }
142
                logger.info("["+callerName+ "] Cannot find translation for key '"+key+"'.");
143
                addNotTranslatedKey(key);
144
                return key;
145
        }
146

    
147
        public static String getText(String key,  String[] arguments, String callerName) {
148
                String translation = getText(key, callerName);
149
                if (translation!=null && arguments!=null ) {
150
                        try {
151
                                translation = MessageFormat.format(translation, arguments);
152
                        }
153
                        catch (IllegalFormatException ex) {
154
                                logger.error(callerName+" -- Error formating key: "+key+" -- "+translation);
155
                        }
156
                }
157
                return translation;
158
        }
159
        
160
        public static String translate(String message, String[] args) {
161
                String msg = message;
162
                if (msg == null) {
163
                        return "";
164
                }
165
                msg = getText(msg, args);
166
                if (msg == null) {
167
                        msg = "_" + message.replace("_", " ");
168
                }
169
                return msg;
170
        }
171

    
172
        public static String translate(String message) {
173
                String msg = message;
174
                if (msg == null) {
175
                        return "";
176
                }
177
                msg = getText(msg, (String[]) null);
178
                if (msg == null || msg.startsWith("_")) {
179
                        msg = "_" + message.replace("_", " ");
180
                }
181
                return msg;
182
        }
183

    
184
        /**
185
         * <p>Gets the localized message associated with the provided key.
186
         * If the key is not in the dictionary or the translation is empty,
187
         * return the key and register the failure in the log.</p>
188
         *
189
         * @param key     An String which identifies the translation that we want to get.
190
         * @return        an String with the message associated with the provided key.
191
         *                If the key is not in the dictionary or the translation is empty,
192
         *                return the key. If the key is null, return null.
193
         */
194
        public static String getText(String key) {
195
                return getText(key, _CLASSNAME);
196
        }
197

    
198
        public static String getText(String key, String[] arguments) {
199
                return getText(key, arguments, _CLASSNAME);
200
        }
201

    
202
        
203
        /**
204
         * <p>Gets the localized message associated with the provided key.
205
         * If the key is not in the dictionary or the translation is empty,
206
         * it returns null and the failure is only registered in the log if
207
         * the param log is true.</p>
208
         *
209
         * @param key        An String which identifies the translation that we want
210
         *                                 to get.
211
         * @param log        Determines whether log a key failure or not
212
         * @return                an String with the message associated with the provided key,
213
         *                                 or null if the key is not in the dictionary or the
214
         *                                 translation is empty.
215
         */
216
        public static String getText(String key, boolean log) {
217
                return getText(key, _CLASSNAME, log);
218
        }
219

    
220
        public static String getText(String key, String[] arguments, boolean log) {
221
                String translation = getText(key, _CLASSNAME, log);
222
                if (translation!=null && arguments!=null ) {
223
                        try {
224
                                translation = MessageFormat.format(translation, arguments);
225
                        } catch (IllegalFormatException ex) {
226
                                if (log) {
227
                                        logger.error(_CLASSNAME+" -- Error formating key: "+key+" -- "+translation);
228
                                }
229
                        }
230
                }
231
                return translation;
232
        }
233

    
234
        /**
235
         * <p>Gets the localized message associated with the provided key.
236
         * If the key is not in the dictionary, it returns null and the failure
237
         * is only registered in the log if the param log is true.</p>
238
         *
239
         * @param key         An String which identifies the translation that we want to get.
240
         * @param callerName  A symbolic name given to the caller of this method, to
241
         *                    show it in the log if the key was not found
242
         * @param log         Determines whether log a key failure or not
243
         * @return            an String with the message associated with the provided key,
244
         *                    or null if the key is not in the dictionary.
245
         */
246
        public static String getText(String key, String callerName, boolean log) {
247
                if (key==null) {
248
                        return null;
249
                }
250
                for (int numLocale=0; numLocale<localeResources.size(); numLocale++) {
251
                        // try to get the translation for any of the languagues in the preferred languages list
252
                        String translation = ((Properties)localeResources.get(numLocale)).getProperty(key);
253
                        if (translation!=null && !translation.equals("")) {
254
                                return translation;
255
                        }
256
                }
257
                if (log) {
258
                        logger.info("["+callerName+"] Cannot find translation for key '"+key+"'.");
259
                }
260
                return null;
261
        }
262

    
263
        public static String getText(String key, String[] arguments, String callerName, boolean log) {
264
                String translation = getText(key, callerName, log);
265
                if (translation!=null) {
266
                        try {
267
                                translation = MessageFormat.format(translation, arguments);
268
                        }
269
                        catch (IllegalFormatException ex) {
270
                                if (log) {
271
                                        logger.error(callerName+" -- Error formating key: "+key+" -- "+translation);
272
                                }
273
                        }
274
                }
275
                return translation;
276
        }
277

    
278
        /**
279
         * <p>Adds an additional family of resource files containing some translations.
280
         * A family is a group of files with a common baseName.
281
         * The file must be an iso-8859-1 encoded file, which can contain any unicode
282
         * character using unicode escaped sequences, and following the syntax:
283
         * <code>key1=value1
284
         * key2=value2</code>
285
         * where 'key1' is the key used to identify the string and must not
286
         * contain the '=' symbol, and 'value1' is the associated translation.</p>
287
         * <p<For example:</p>
288
         * <code>cancel=Cancelar
289
         * accept=Aceptar</code>
290
         * <p>Only one pair key-value is allowed per line.</p>
291
         *
292
         * <p>The actual name of the resource file to load is determined using the rules
293
         * explained in the class java.util.ResourceBundle. Summarizing, for each language
294
         * in the specified preferred locales list it will try to load a file with
295
         *  the following structure: <code>family_locale.properties</code></p>
296
         *
297
         * <p>For example, if the preferred locales list contains {"fr", "es", "en"}, and
298
         * the family name is "text", it will try to load the files "text_fr.properties",
299
         * "text_es.properties" and finally "text_en.properties".</p>
300
         *
301
         * <p>Locales might be more specific, such us "es_AR"  (meaning Spanish from Argentina)
302
         * or "es_AR_linux" (meaning Linux system preferring Spanish from Argentina). In the
303
         * later case, it will try to load "text_es_AR_linux.properties", then
304
         * "text_es_AR.properties" if the former fails, and finally "text_es.properties".</p>
305
         *
306
         * <p>The directory used to locate the resource file is determining by using the
307
         * getResource method from the provided ClassLoader.</p>
308
         *
309
         * @param family    The family name (or base name) which is used to search
310
         *                  actual properties files.
311
         * @param loader    A ClassLoader which is able to find a property file matching
312
         *                                         the specified family name and the preferred locales
313
         * @see             <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/ResourceBundle.html">ResourceBundle</a>
314
         */
315
        public static void addResourceFamily(String family, ClassLoader loader) {
316
                addResourceFamily(family, loader, "");
317
        }
318

    
319
        /**
320
         * <p>Adds an additional family of resource files containing some translations.
321
         * The search path to locate the files is provided by the dirList parameter.</p>
322
         *
323
         * <p>See {@link addResourceFamily(String, ClassLoader)} for a discussion about the
324
         * format of the property files and the way to determine the candidat files
325
         * to load. Note that those methods are different in the way to locate the
326
         * candidat files. This method searches in the provided paths (<code>dirList</code>
327
         * parameter), while the referred method searches using the getResource method
328
         * of the provided ClassLoader.</p>
329
         *
330
         * @param family    The family name (or base name) which is used to search
331
         *                  actual properties files.
332
         * @param dirList   A list of search paths to locate the property files
333
         * @throws MalformedURLException
334
         * @see             <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/ResourceBundle.html">ResourceBundle</a>
335
         */
336
        public static void addResourceFamily(String family, File[] dirList) throws MalformedURLException{
337
                // use our own classloader
338
                URL[] urls = new URL[dirList.length];
339

    
340
                        int i;
341
                        for (i=0; i<urls.length; i++) {
342
                                urls[i] = dirList[i].toURL();
343
                        }
344

    
345
                ClassLoader loader = new MessagesClassLoader(urls);
346
                addResourceFamily(family, loader, "");
347
        }
348

    
349
        /**
350
         * <p>Adds an additional family of resource files containing some translations.
351
         * The search path to locate the files is provided by the dir parameter.</p>
352
         *
353
         * <p>See {@link addResourceFamily(String, ClassLoader)} for a discussion about the
354
         * format of the property files and the way to determine the candidat files
355
         * to load. Note that those methods are different in the way to locate the
356
         * candidat files. This method searches in the provided path (<code>dir</code>
357
         * parameter), while the referred method searches using the getResource method
358
         * of the provided ClassLoader.</p>
359
         *
360
         * @param family    The family name (or base name) which is used to search
361
         *                  actual properties files.
362
         * @param dir       The search path to locate the property files
363
         * @throws MalformedURLException
364
         * @see             <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/ResourceBundle.html">ResourceBundle</a>
365
         */
366
        public static void addResourceFamily(String family, File dir) throws MalformedURLException{
367
                // use our own classloader
368
                URL[] urls = new URL[1];
369
                urls[0] = dir.toURL();
370
                ClassLoader loader = new MessagesClassLoader(urls);
371
                addResourceFamily(family, loader, "");
372
        }
373

    
374

    
375
        /**
376
         * <p>Adds an additional family of resource files containing some translations.
377
         * The search path is determined by the getResource method from the
378
         * provided ClassLoader.</p>
379
         *
380
         * <p>This method is identical to {@link addResourceFamily(String, ClassLoader)},
381
         * except that it adds a <pode>callerName</code> parameter to show in the log.</p>
382
         *
383
         * <p>See {@link addResourceFamily(String, ClassLoader)} for a discussion about the
384
         * format of the property files andthe way to determine the candidat files
385
         * to load.</p>
386
         *
387
         * @param family      The family name (or base name) which is used to search
388
         *                    actual properties files.
389
         * @param loader      A ClassLoader which is able to find a property file matching
390
         *                                           the specified family name and the preferred locales
391
         * @param callerName  A symbolic name given to the caller of this method, to
392
         *                    show it in the log if there is an error
393
         * @see               <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/ResourceBundle.html">ResourceBundle</a>
394
         */
395
        public static void addResourceFamily(String family, ClassLoader loader, String callerName) {
396
//                String currentKey;
397
//                Enumeration keys;
398
                Locale lang;
399
//                Properties properties;
400
                Properties translations;
401
                int totalLocales = preferredLocales.size();
402

    
403
                if (totalLocales == 0) {
404
                        // if it's empty, warn about that
405
                        logger.warn("There is not preferred languages list. Maybe the Messages class was not initialized");
406
                }
407

    
408
                familyDescriptors.add( new FamilyDescriptor(family,loader,callerName));
409
                
410
                resourceFamilies.add(family);
411
                classLoaders.add(loader);
412

    
413
                for (int numLocale=0; numLocale<totalLocales; numLocale++) { // for each language
414
//                        properties =  new Properties();
415

    
416
                        lang = (Locale) preferredLocales.get(numLocale);
417
                        translations = (Properties) localeResources.get(numLocale);
418

    
419
                        addResourceFamily(lang, translations, family, loader, callerName);
420
                }
421
        }
422
        
423
        private static void addResourceFamily(Locale lang, Properties translations,
424
                        String family, ClassLoader loader, String callerName) {
425
                logger.debug("addResourceFamily "+lang.toString()+", "+family+", "+loader.toString());
426
                
427
                Properties properties = new Properties();
428
                String langCode = lang.toString();
429
                String resource = family.replace('.', '/') + "_" + langCode + ".properties";
430
                URL resourceURL = loader.getResource(resource);
431
                InputStream is = loader.getResourceAsStream(resource);
432
                if( is==null && langCode.contains("_") ) {
433
                        try {
434
                                langCode = langCode.split("_")[0];
435
                                resource = family.replace('.', '/') + "_" + langCode + ".properties";
436
                                resourceURL = loader.getResource(resource);
437
                                is = loader.getResourceAsStream(resource);
438
                                if( is==null ) {
439
                                        resource = family.replace('.', '/') +  ".properties";
440
                                        resourceURL = loader.getResource(resource);
441
                                        is = loader.getResourceAsStream(resource);
442
                                }
443
                        } catch(Exception ex) {
444
                                // Do nothing, is are null and are handled later
445
                        }
446
                }
447
                if (is != null) {
448
                        try {
449
                                properties.load(is);
450
                        } catch (IOException e) {
451
                        }
452
                } else if (lang.equals(baseLocale)) {
453
                        // try also "text.properties" for the base language
454
                        is = loader.getResourceAsStream(family.replace('.', '/')
455
                                        + ".properties");
456

    
457

    
458
                        if (is != null) {
459
                                try {
460
                                        properties.load(is);
461
                                } catch (IOException e) {
462
                                }
463
                        }
464

    
465
                }
466
                if( resourceURL!=null && logger.isDebugEnabled() ) {
467
                    logger.debug("Load resources from '"+resourceURL.toString()+"' with classloader {"+loader.toString()+"}.");
468
                }
469
                Enumeration keys = properties.keys();
470
                while (keys.hasMoreElements()) {
471
                        String currentKey = (String) keys.nextElement();
472
                        if (!translations.containsKey(currentKey)) {
473
                                translations.put(currentKey, properties.getProperty(currentKey));
474
                        }
475
                }
476

    
477
        }
478

    
479
        /**
480
         * <p>Adds an additional family of resource files containing some translations.</p>
481
         *
482
         * <p>This method is identical to {@link addResourceFamily(String, ClassLoader, String)},
483
         * except that it uses the caller's class loader.</p>
484
         *
485
         * <p>See {@link addResourceFamily(String, ClassLoader)} for a discussion about the
486
         * format of the property files and the way to determine the candidat files
487
         * to load.</p>
488
         *
489
         * @param family      The family name (or base name) which is used to search
490
         *                    actual properties files.
491
         * @param callerName  A symbolic name given to the caller of this method, to
492
         *                    show it in the log if there is an error. This is only used
493
         *                    to show
494
         *                    something meaningful in the log, so you can use any string
495
         * @see               <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/ResourceBundle.html">ResourceBundle</a>
496
         */
497
        public static void addResourceFamily(String family, String callerName) {
498
                addResourceFamily(family, Messages.class.getClassLoader(), callerName);
499
        }
500

    
501

    
502
        /**
503
         * Returns an ArrayList containing the ordered list of prefered Locales
504
         * Each element of the ArrayList is a Locale object.
505
         *
506
         * @return an ArrayList containing the ordered list of prefered Locales
507
         * Each element of the ArrayList is a Locale object.
508
         */
509
        public static ArrayList getPreferredLocales() {
510
                return preferredLocales;
511
        }
512

    
513
        /**
514
         * <p>Sets the ordered list of preferred locales.
515
         * Each element of the ArrayList is a Locale object.</p>
516
         *
517
         * <p>Note that calling this method does not load any translation, it just
518
         * adds the language to the preferred locales list, so this method must
519
         * be always called before the translations are loaded using
520
         * the addResourceFamily() methods.</p>
521
         *
522
         * <p>It there was any language in the preferred locale list, the language
523
         * and its associated translations are deleted.</p>
524
         *
525
         *
526
         * @param preferredLocales an ArrayList containing Locale objects.
527
         * The ArrayList represents an ordered list of preferred locales
528
         */
529
        public static void setPreferredLocales(ArrayList preferredLocalesList) {
530
                logger.info("setPreferredLocales "+preferredLocalesList.toString());
531
                // delete all existing locales
532
                Iterator oldLocales = preferredLocales.iterator();
533
                while (oldLocales.hasNext()) {
534
                        removeLocale((Locale) oldLocales.next());
535
                }
536

    
537
                // add the new locales now
538
                for (int numLocale=0; numLocale < preferredLocalesList.size(); numLocale++) {
539
                        addLocale((Locale) preferredLocalesList.get(numLocale));
540
                }
541
        }
542

    
543
        public static Locale getCurrentLocale() {
544
            return currentLocale;
545
        }
546

    
547
        /**
548
         * 
549
         * @deprecated  use setCurrentLocale(Locale locale, Locale alternatives[]) or LocaleManager.setCurrentLocale
550
         */
551
        public static void setCurrentLocale(Locale locale) {
552
            Locale alternatives[] = null;
553

    
554
            String localeStr = locale.getLanguage();
555
            if ( localeStr.equals("es") || 
556
                 localeStr.equals("ca") ||
557
                 localeStr.equals("gl") || 
558
                 localeStr.equals("eu") ||
559
                 localeStr.equals("vl") ) {
560
                alternatives = new Locale[2];
561
                alternatives[0] = new Locale("es");
562
                alternatives[1] = new Locale("en");
563
            } else {
564
                // prefer English for the rest
565
                alternatives = new Locale[2];
566
                alternatives[0] = new Locale("en");
567
                alternatives[1] = new Locale("es");
568
            }
569
            setCurrentLocale(locale, alternatives);
570
        }
571
        
572
        public static void setCurrentLocale(Locale locale, Locale alternatives[]) {
573
            logger.info("setCurrentLocale "+locale.toString());
574
            
575
            resourceFamilies = new HashSet();
576
            classLoaders = new HashSet();
577
            localeResources = new ArrayList();
578
            preferredLocales = new ArrayList();
579
            notTranslatedKeys = new HashSet();            
580
            
581
            addLocale(locale);
582
            for( int i=0 ; i<alternatives.length; i++ ) {
583
                addLocale(alternatives[i]);
584
            }
585
            for( int curlocale=0; curlocale<preferredLocales.size(); curlocale++) {
586
                for( int curfamily=0; curfamily<familyDescriptors.size(); curfamily++) {
587
                     FamilyDescriptor family = (FamilyDescriptor) familyDescriptors.get(curfamily);
588
                     addResourceFamily(
589
                             (Locale) preferredLocales.get(curlocale),
590
                             (Properties) localeResources.get(curlocale),
591
                             family.family,
592
                             family.loader,
593
                             family.callerName);
594
                }
595
            }
596
            currentLocale = locale;
597
            Locale.setDefault(locale);
598
        }
599

    
600
        /**
601
         * Adds a Locale at the end of the ordered list of preferred locales.
602
         * Note that calling this method does not load any translation, it just
603
         * adds the language to the preferred locales list, so this method must
604
         * be always called before the translations are loaded using
605
         * the addResourceFamily() methods.
606
         *
607
         * @param lang   A Locale object specifying the locale to add
608
         */
609
        public static void addLocale(Locale lang) {
610
                if (!preferredLocales.contains(lang)) { // avoid duplicates
611
                    logger.info("addLocale "+lang.toString());
612
                    preferredLocales.add(lang); // add the lang to the ordered list of preferred locales
613
                    Properties dict = new Properties();
614
                    localeResources.add(dict); // add a hashmap which will contain the translation for this language
615
                }
616
        }
617

    
618
        /**
619
         * Removes the specified Locale from the list of preferred locales and the
620
         * translations associated with this locale.
621
         *
622
         * @param lang   A Locale object specifying the locale to remove
623
         * @return       True if the locale was in the preferred locales list, false otherwise
624
         */
625
        public static boolean removeLocale(Locale lang) {
626
                int numLocale = preferredLocales.indexOf(lang);
627
                if (numLocale!=-1) { // we found the locale in the list
628
                        try {
629
                                preferredLocales.remove(numLocale);
630
                                localeResources.remove(numLocale);
631
                        }
632
                        catch (IndexOutOfBoundsException ex) {
633
                                logger.warn(_CLASSNAME + "." + "removeLocale: " + ex.getLocalizedMessage(), ex);
634
                        }
635
                        return true;
636
                }
637
                return false;
638
        }
639

    
640
        /**
641
         * Cleans the translation tables (removes all the translations from memory).
642
         */
643
        public static void removeResources() {
644
                for (int numLocale=0; numLocale<localeResources.size(); numLocale++) {
645
                        ((Properties)localeResources.get(numLocale)).clear();
646
                }
647
        }
648

    
649
        /**
650
         * The number of translation keys which have been loaded till now
651
         * (In other words: the number of available translation strings).
652
         *
653
         * @param lang The language for which we want to know the number of translation keys
654
         * return The number of translation keys for the provided language.
655
         */
656
        protected static int size(Locale lang) {
657
                int numLocale = preferredLocales.indexOf(lang);
658
                if (numLocale!=-1) {
659
                        return ((Properties)localeResources.get(numLocale)).size();
660
                };
661
                return 0;
662
        }
663

    
664
        protected static Set keySet(Locale lang) {
665
                int numLocale = preferredLocales.indexOf(lang);
666
                if (numLocale!=-1) {
667
                        return ((Properties)localeResources.get(numLocale)).keySet();
668
                } else {
669
                        return null;
670
                }
671
        }
672

    
673
        /**
674
         * Checks if some locale has been added to the preferred locales
675
         * list, which is necessary before loading any translation because
676
         * only the translations for the preferred locales are loaded.
677
         *
678
         * @return
679
         */
680
        public static boolean hasLocales() {
681
                return preferredLocales.size()>0;
682
        }
683

    
684
        /**
685
         * Gets the base language, the language considered the origin of
686
         * translations, which will be (possibly) stored in a property
687
         * file without language suffix
688
         * (ie: text.properties instead of text_es.properties).
689
         */
690
        public static String getBaseLanguage() {
691
                return baseLanguage;
692
        }
693

    
694
        /**
695
         * Sets the base language, the language considered the origin of
696
         * translations, which will be (possibly)
697
         * stored in a property file without language suffix
698
         * (ie: text.properties instead of text_es.properties).
699
         *
700
         * @param lang The base language to be set
701
         */
702
        public static void setBaseLanguage(String lang) {
703
                baseLanguage = lang;
704
                baseLocale = new Locale(baseLanguage);
705
        }
706

    
707
        /*
708
         * Searches the subdirectories of the provided directory, finding
709
         * all the translation files, and constructing a list of available translations.
710
         * It reports different country codes or variants, if available.
711
         * For example, if there is an en_US translation and an en_GB translation, both
712
         * locales will be present in the Vector.
713
         *
714
         * @return
715
         */
716

    
717
        /**
718
         *
719
         * @return A Vector containing the available locales. Each element is a Locale object
720
         */
721
        /*public static Vector getAvailableLocales() {
722
                return _availableLocales;
723
        }*/
724

    
725
        /**
726
         *
727
         * @return A Vector containing the available languages. Each element is an String object
728
         */
729
        /*public static Vector getAvailableLanguages() {
730
                Vector availableLanguages = new Vector();
731
                Locale lang;
732
                Enumeration locales = _availableLocales.elements();
733
                while (locales.hasMoreElements()) {
734
                        lang = (Locale) locales.nextElement();
735
                        availableLanguages.add(lang.getLanguage());
736
                }
737
                return availableLanguages;
738
        }*/
739

    
740
        public static Properties getAllTexts(Locale lang) {
741
                Properties texts = new Properties();
742
                getAllTexts(lang, null, texts);
743
                for (Iterator iterator = classLoaders.iterator(); iterator.hasNext();) {
744
                        getAllTexts(lang, (ClassLoader) iterator.next(), texts);
745
                }
746
                return texts;
747
        }
748

    
749
        private static void getAllTexts(Locale lang, ClassLoader classLoader,
750
                        Properties texts) {
751
                ClassLoader loader = classLoader == null ? Messages.class
752
                                .getClassLoader() : classLoader;
753

    
754
                for (Iterator iterator = resourceFamilies.iterator(); iterator
755
                                .hasNext();) {
756
                        String family = (String) iterator.next();
757
                        addResourceFamily(lang, texts, family, loader,
758
                                        "Messages.getAllTexts");
759
                }
760
        }
761

    
762
        
763
        public static Properties getTranslations(Locale locale) {
764
                Properties translations = new Properties();
765
                for( int curfamily=0; curfamily<familyDescriptors.size(); curfamily++) {
766
                     FamilyDescriptor family = (FamilyDescriptor) familyDescriptors.get(curfamily);
767
                     addResourceFamily(
768
                             locale,
769
                             translations,
770
                             family.family,
771
                             family.loader,
772
                             family.callerName);
773
                }
774
                return translations;
775
        }
776
        
777
        private static void addNotTranslatedKey(String key) {
778
            notTranslatedKeys.add(key);
779
        }
780
        
781
        public static List getNotTranslatedKeys() {
782
            List l = new ArrayList(notTranslatedKeys);
783
            return l;
784
        }
785
}