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 @ 42543

History | View | Annotate | Download (28.6 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
                addNotTranslatedKey(key, callerName, true);
143
                return key;
144
        }
145

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

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

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

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

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

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

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

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

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

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

    
337
                        int i;
338
                        for (i=0; i<urls.length; i++) {
339
                                urls[i] = dirList[i].toURL();
340
                        }
341

    
342
                ClassLoader loader = new MessagesClassLoader(urls);
343
                addResourceFamily(family, loader, "");
344
        }
345

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

    
371

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

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

    
405
                familyDescriptors.add( new FamilyDescriptor(family,loader,callerName));
406
                
407
                resourceFamilies.add(family);
408
                classLoaders.add(loader);
409

    
410
                for (int numLocale=0; numLocale<totalLocales; numLocale++) { // for each language
411
//                        properties =  new Properties();
412

    
413
                        lang = (Locale) preferredLocales.get(numLocale);
414
                        translations = (Properties) localeResources.get(numLocale);
415

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

    
454

    
455
                        if (is != null) {
456
                                try {
457
                                        properties.load(is);
458
                                } catch (IOException e) {
459
                                }
460
                        }
461

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

    
474
        }
475

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

    
498

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

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

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

    
540
        public static Locale getCurrentLocale() {
541
            return currentLocale;
542
        }
543

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

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

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

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

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

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

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

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

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

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

    
706

    
707
        public static Properties getAllTexts(Locale lang) {
708
                Properties texts = new Properties();
709
                getAllTexts(lang, null, texts);
710
                for (Iterator iterator = classLoaders.iterator(); iterator.hasNext();) {
711
                        getAllTexts(lang, (ClassLoader) iterator.next(), texts);
712
                }
713
                return texts;
714
        }
715

    
716
        private static void getAllTexts(Locale lang, ClassLoader classLoader,
717
                        Properties texts) {
718
                ClassLoader loader = classLoader == null ? Messages.class
719
                                .getClassLoader() : classLoader;
720

    
721
                for (Iterator iterator = resourceFamilies.iterator(); iterator
722
                                .hasNext();) {
723
                        String family = (String) iterator.next();
724
                        addResourceFamily(lang, texts, family, loader,
725
                                        "Messages.getAllTexts");
726
                }
727
        }
728

    
729
        
730
        public static Properties getTranslations(Locale locale) {
731
                Properties translations = new Properties();
732
                for( int curfamily=0; curfamily<familyDescriptors.size(); curfamily++) {
733
                     FamilyDescriptor family = (FamilyDescriptor) familyDescriptors.get(curfamily);
734
                     addResourceFamily(
735
                             locale,
736
                             translations,
737
                             family.family,
738
                             family.loader,
739
                             family.callerName);
740
                }
741
                return translations;
742
        }
743

    
744
        private static void addNotTranslatedKey(String key, String callerName, boolean log) {
745
            if (!notTranslatedKeys.contains(key)) {
746
                if( log ) {
747
                    logger.info("[" + callerName + "] Cannot find translation for key '" + key + "'.");
748
                }
749
                notTranslatedKeys.add(key);
750
            }
751
        }
752
        
753
        public static List getNotTranslatedKeys() {
754
            List l = new ArrayList(notTranslatedKeys);
755
            return l;
756
        }
757
}