Statistics
| Revision:

svn-gvsig-desktop / branches / v10 / libraries / libInternationalization / src / org / gvsig / i18n / Messages.java @ 25959

History | View | Annotate | Download (27.8 KB)

1
/* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
2
 *
3
 * Copyright (C) 2006 IVER T.I. and Generalitat Valenciana.
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
18
 *
19
 * For more information, contact:
20
 *
21
 *  Generalitat Valenciana
22
 *   Conselleria d'Infraestructures i Transport
23
 *   Av. Blasco Ib??ez, 50
24
 *   46010 VALENCIA
25
 *   SPAIN
26
 *
27
 *      +34 963862235
28
 *   gvsig@gva.es
29
 *      www.gvsig.gva.es
30
 *
31
 *    or
32
 *
33
 *   IVER T.I. S.A
34
 *   Salamanca 50
35
 *   46005 Valencia
36
 *   Spain
37
 *
38
 *   +34 963163400
39
 *   dac@iver.es
40
 */
41

    
42
package org.gvsig.i18n;
43

    
44
import java.io.File;
45
import java.net.MalformedURLException;
46
import java.net.URL;
47
import java.util.*;
48

    
49
import org.apache.log4j.Logger;
50

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

    
93
    /*
94
     * Each entry will contain a hashmap with translations. Each hasmap contains
95
     * the translations for one language, indexed by the translation key. The
96
     * translations for language (i) in the preferred locales list are contained
97
     * in the position (i) of the localeResources list
98
     */
99
    private static List localeResources = new ArrayList();
100
    /*
101
     * contains the ordered list of prefered languages/locales (class Locale)
102
     */
103
    private static List preferredLocales = new ArrayList();
104
    
105
    private static Set resourceFamilies = new HashSet();
106
    
107
    private static Set classLoaders = new HashSet();
108

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

    
151
    /**
152
     * <p>
153
     * Gets the localized message associated with the provided key. If the key
154
     * is not in the dictionary or the translation is empty, return the key and
155
     * register the failure in the log.
156
     * </p>
157
     * 
158
     * @param key
159
     *            An String which identifies the translation that we want to
160
     *            get.
161
     * @return an String with the message associated with the provided key. If
162
     *         the key is not in the dictionary or the translation is empty,
163
     *         return the key. If the key is null, return null.
164
     */
165
    public static String getText(String key) {
166
        return getText(key, _CLASSNAME);
167
    }
168

    
169
    /**
170
     * <p>
171
     * Gets the localized message associated with the provided key. If the key
172
     * is not in the dictionary or the translation is empty, it returns null and
173
     * the failure is only registered in the log if the param log is true.
174
     * </p>
175
     * 
176
     * @param key
177
     *            An String which identifies the translation that we want to
178
     *            get.
179
     * @param log
180
     *            Determines whether log a key failure or not
181
     * @return an String with the message associated with the provided key, or
182
     *         null if the key is not in the dictionary or the translation is
183
     *         empty.
184
     */
185
    public static String getText(String key, boolean log) {
186
        return getText(key, _CLASSNAME, log);
187
    }
188

    
189
    /**
190
     * <p>
191
     * Gets the localized message associated with the provided key. If the key
192
     * is not in the dictionary, it returns null and the failure is only
193
     * registered in the log if the param log is true.
194
     * </p>
195
     * 
196
     * @param key
197
     *            An String which identifies the translation that we want to
198
     *            get.
199
     * @param callerName
200
     *            A symbolic name given to the caller of this method, to show it
201
     *            in the log if the key was not found
202
     * @param log
203
     *            Determines whether log a key failure or not
204
     * @return an String with the message associated with the provided key, or
205
     *         null if the key is not in the dictionary.
206
     */
207
    public static String getText(String key, String callerName, boolean log) {
208
        String text = null;
209
        if (key != null) {
210
            for (int numLocale = 0; numLocale < localeResources.size(); numLocale++) {
211
                // try to get the translation for any of the languagues in the
212
                // preferred languages list
213
                String translation = (String) ((HashMap) localeResources
214
                        .get(numLocale)).get(key);
215
                if (translation != null && !translation.equals("")) {
216
                    text = translation;
217
                    break;
218
                }
219
            }
220
            if (text == null && log) {
221
                logger.warn(callerName
222
                        + " -- "
223
                        + Messages.getText("No_se_encontro_la_traduccion_para",
224
                                _CLASSNAME, false) + " " + key);
225
            }
226
        }
227
        // logger.debug("Messages: " + key + " = " + text);
228
        return text;
229
    }
230

    
231
    /**
232
     * <p>
233
     * Adds an additional family of resource files containing some translations.
234
     * A family is a group of files with a common baseName. The file must be an
235
     * iso-8859-1 encoded file, which can contain any unicode character using
236
     * unicode escaped sequences, and following the syntax: <code>key1=value1
237
     * key2=value2</code> where 'key1' is the key used to identify the string
238
     * and must not contain the '=' symbol, and 'value1' is the associated
239
     * translation.
240
     * </p>
241
     * <p<For example:</p> <code>cancel=Cancelar
242
     * accept=Aceptar</code>
243
     * <p>
244
     * Only one pair key-value is allowed per line.
245
     * </p>
246
     * 
247
     * <p>
248
     * The actual name of the resource file to load is determined using the
249
     * rules explained in the class java.util.ResourceBundle. Summarizing, for
250
     * each language in the specified preferred locales list it will try to load
251
     * a file with the following structure:
252
     * <code>family_locale.properties</code>
253
     * </p>
254
     * 
255
     * <p>
256
     * For example, if the preferred locales list contains {"fr", "es", "en"},
257
     * and the family name is "text", it will try to load the files
258
     * "text_fr.properties", "text_es.properties" and finally
259
     * "text_en.properties".
260
     * </p>
261
     * 
262
     * <p>
263
     * Locales might be more specific, such us "es_AR" (meaning Spanish from
264
     * Argentina) or "es_AR_linux" (meaning Linux system preferring Spanish from
265
     * Argentina). In the later case, it will try to load
266
     * "text_es_AR_linux.properties", then "text_es_AR.properties" if the former
267
     * fails, and finally "text_es.properties".
268
     * </p>
269
     * 
270
     * <p>
271
     * The directory used to locate the resource file is determining by using
272
     * the getResource method from the provided ClassLoader.
273
     * </p>
274
     * 
275
     * @param family
276
     *            The family name (or base name) which is used to search actual
277
     *            properties files.
278
     * @param loader
279
     *            A ClassLoader which is able to find a property file matching
280
     *            the specified family name and the preferred locales
281
     * @see <a
282
     *      href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/ResourceBundle.html">ResourceBundle</a>
283
     */
284
    public static void addResourceFamily(String family, ClassLoader loader) {
285
        addResourceFamily(family, loader, "");
286
    }
287

    
288
    /**
289
     * <p>
290
     * Adds an additional family of resource files containing some translations.
291
     * The search path to locate the files is provided by the dirList parameter.
292
     * </p>
293
     * 
294
     * <p>
295
     * See {@link addResourceFamily(String, ClassLoader)} for a discussion about
296
     * the format of the property files and the way to determine the candidat
297
     * files to load. Note that those methods are different in the way to locate
298
     * the candidat files. This method searches in the provided paths (
299
     * <code>dirList</code> parameter), while the referred method searches using
300
     * the getResource method of the provided ClassLoader.
301
     * </p>
302
     * 
303
     * @param family
304
     *            The family name (or base name) which is used to search actual
305
     *            properties files.
306
     * @param dirList
307
     *            A list of search paths to locate the property files
308
     * @throws MalformedURLException
309
     * @see <a
310
     *      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, File[] dirList)
313
            throws MalformedURLException {
314
        // use our own classloader
315
        URL[] urls = new URL[dirList.length];
316

    
317
        int i;
318
        for (i = 0; i < urls.length; i++) {
319
            urls[i] = dirList[i].toURL();
320
        }
321

    
322
        ClassLoader loader = new MessagesClassLoader(urls);
323
        addResourceFamily(family, loader, "");
324
    }
325

    
326
    /**
327
     * <p>
328
     * Adds an additional family of resource files containing some translations.
329
     * The search path to locate the files is provided by the dir parameter.
330
     * </p>
331
     * 
332
     * <p>
333
     * See {@link addResourceFamily(String, ClassLoader)} for a discussion about
334
     * the format of the property files and the way to determine the candidat
335
     * files to load. Note that those methods are different in the way to locate
336
     * the candidat files. This method searches in the provided path (
337
     * <code>dir</code> parameter), while the referred method searches using the
338
     * getResource method of the provided ClassLoader.
339
     * </p>
340
     * 
341
     * @param family
342
     *            The family name (or base name) which is used to search actual
343
     *            properties files.
344
     * @param dir
345
     *            The search path to locate the property files
346
     * @throws MalformedURLException
347
     * @see <a
348
     *      href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/ResourceBundle.html">ResourceBundle</a>
349
     */
350
    public static void addResourceFamily(String family, File dir)
351
            throws MalformedURLException {
352
        // use our own classloader
353
        URL[] urls = new URL[1];
354
        urls[0] = dir.toURL();
355
        ClassLoader loader = new MessagesClassLoader(urls);
356
        addResourceFamily(family, loader, "");
357
    }
358

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

    
401
        if (totalLocales == 0) {
402
            // if it's empty, warn about that
403
            logger
404
                    .warn(Messages
405
                            .getText(
406
                                    "No_hay_lista_de_idiomas_preferidos_quiza_olvido_inicializar_clase",
407
                                    _CLASSNAME, false));
408
        }
409
        Locale lang;
410
        ResourceBundle bundle = null;
411

    
412
        for (int numLocale = 0; numLocale < totalLocales; numLocale++) { // for
413
            // each
414
            // language
415
            lang = (Locale) preferredLocales.get(numLocale);
416
            try {
417
                /**
418
                 * Here we are doing a pervert use of getBundle method.
419
                 * Normally, it is call it in in this way: getBundle("text",
420
                 * "en", loader), in order to get the file 'text_en.properties'.
421
                 * However, in this way the default file ('text.properties') is
422
                 * also loaded, and we want to avoid this (because
423
                 * 'text.properties contains the Spanish translation). So we use
424
                 * the method in this non-standard way: getBundle("text_en",
425
                 * "en", loader), ensuring that we are just loading the file
426
                 * 'text_en.properties'
427
                 **/
428
                if (!lang.getLanguage().equals("es"))
429
                    bundle = ResourceBundle.getBundle(family + "_"
430
                            + lang.getLanguage(), lang, loader);
431
                else { // we allow 'text.properties' to be loaded for Spanish
432
                    if (Locale.getDefault().equals(lang)) {
433
                        bundle = ResourceBundle.getBundle(family, lang, loader);
434
                    } else {
435
                        // we need to temporarily change the default locale to
436
                        // properly load spanish in corner cases
437
                        Locale.setDefault(lang);
438
                        bundle = ResourceBundle.getBundle(family, lang, loader);
439
                        Locale.setDefault(defaultLocale);
440
                    }
441
                }
442

    
443
                properties = bundle.getKeys();
444
                while (properties.hasMoreElements()) {
445
                    currentKey = (String) properties.nextElement();
446
                    if (!((HashMap) localeResources.get(numLocale))
447
                            .containsKey(currentKey)) {
448
                        ((HashMap) localeResources.get(numLocale)).put(
449
                                currentKey, bundle.getString(currentKey));
450
                    }
451
                }
452
            } catch (MissingResourceException ex) {
453
                Locale.setDefault(defaultLocale);
454
                logger.warn(Messages.getText(
455
                        "Las_traducciones_no_pudieron_ser_cargadas", false)
456
                        + " -- " + callerName + " -- " + lang.getLanguage());
457
            }
458
        }
459
    }
460

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

    
491
        String currentKey;
492
        Enumeration properties;
493
        Locale defaultLocale = Locale.getDefault();
494
        int totalLocales = preferredLocales.size();
495

    
496
        if (totalLocales == 0) {
497
            // if it's empty, warn about that
498
            logger
499
                    .warn(Messages
500
                            .getText(
501
                                    "No_hay_lista_de_idiomas_preferidos_quiza_olvido_inicializar_clase",
502
                                    _CLASSNAME, false));
503
        }
504
        Locale lang;
505
        ResourceBundle bundle = null;
506

    
507
        for (int numLocale = 0; numLocale < totalLocales; numLocale++) { // for
508
            // each
509
            // language
510
            try {
511
                lang = (Locale) preferredLocales.get(numLocale);
512
                if (!lang.getLanguage().equals("es")) // ensure we don't get a
513
                    // fallback
514
                    bundle = ResourceBundle.getBundle(family + "_"
515
                            + lang.getLanguage(), lang);
516
                else { // we allow 'text.properties' to be loaded for Spanish
517
                    if (Locale.getDefault().equals(lang)) {
518
                        bundle = ResourceBundle.getBundle(family, lang);
519
                    } else {
520
                        // we need to temporarily change the default locale to
521
                        // properly load spanish in corner cases
522
                        Locale.setDefault(lang);
523
                        bundle = ResourceBundle.getBundle(family, lang);
524
                        Locale.setDefault(defaultLocale);
525
                    }
526
                }
527

    
528
                properties = bundle.getKeys();
529
                while (properties.hasMoreElements()) {
530
                    currentKey = (String) properties.nextElement();
531
                    if (!((HashMap) localeResources.get(numLocale))
532
                            .containsKey(currentKey)) {
533
                        ((HashMap) localeResources.get(numLocale)).put(
534
                                currentKey, bundle.getString(currentKey));
535
                    }
536
                }
537
            } catch (MissingResourceException ex) {
538
                Locale.setDefault(defaultLocale);
539
                logger.warn(callerName + " " + ex.getLocalizedMessage());
540
            }
541
        }
542
    }
543

    
544
    private static void addResourceFamily(String family, Locale lang, Map dict) {
545
        
546
        resourceFamilies.add(family);
547

    
548
        String currentKey;
549
        Enumeration properties;
550

    
551
        ResourceBundle bundle = null;
552
        Locale defaultLocale = Locale.getDefault();
553

    
554
        try {
555
            if (!lang.getLanguage().equals("es")) // ensure we don't get a
556
                // fallback
557
                bundle = ResourceBundle.getBundle(family + "_"
558
                        + lang.getLanguage(), lang);
559
            else { // we allow 'text.properties' to be loaded for Spanish
560
                if (Locale.getDefault().equals(lang)) {
561
                    bundle = ResourceBundle.getBundle(family, lang);
562
                } else {
563
                    // we need to temporarily change the default locale to
564
                    // properly load spanish in corner cases
565
                    Locale.setDefault(lang);
566
                    bundle = ResourceBundle.getBundle(family, lang);
567
                    Locale.setDefault(defaultLocale);
568
                }
569
            }
570
            properties = bundle.getKeys();
571
            while (properties.hasMoreElements()) {
572
                currentKey = (String) properties.nextElement();
573
                if (!dict.containsKey(currentKey)) {
574
                    dict.put(currentKey, bundle.getString(currentKey));
575
                }
576
            }
577
        } catch (MissingResourceException ex) {
578
            Locale.setDefault(defaultLocale);
579
            logger.warn(Messages.class.getName() + " "
580
                    + ex.getLocalizedMessage());
581
        }
582
    }
583

    
584
    private static void addResourceFamily(String family, Locale lang, Map dict, ClassLoader classLoader) {
585
        
586
        resourceFamilies.add(family);
587

    
588
        String currentKey;
589
        Enumeration properties;
590

    
591
        ResourceBundle bundle = null;
592
        Locale defaultLocale = Locale.getDefault();
593

    
594
        try {
595
            if (!lang.getLanguage().equals("es")) // ensure we don't get a
596
                // fallback
597
                bundle = ResourceBundle.getBundle(family + "_"
598
                        + lang.getLanguage(), lang, classLoader);
599
            else { // we allow 'text.properties' to be loaded for Spanish
600
                if (Locale.getDefault().equals(lang)) {
601
                    bundle = ResourceBundle.getBundle(family, lang, classLoader);
602
                } else {
603
                    // we need to temporarily change the default locale to
604
                    // properly load spanish in corner cases
605
                    Locale.setDefault(lang);
606
                    bundle = ResourceBundle.getBundle(family, lang, classLoader);
607
                    Locale.setDefault(defaultLocale);
608
                }
609
            }
610
            properties = bundle.getKeys();
611
            while (properties.hasMoreElements()) {
612
                currentKey = (String) properties.nextElement();
613
                if (!dict.containsKey(currentKey)) {
614
                    dict.put(currentKey, bundle.getString(currentKey));
615
                }
616
            }
617
        } catch (MissingResourceException ex) {
618
            Locale.setDefault(defaultLocale);
619
            logger.warn(Messages.class.getName() + " "
620
                    + ex.getLocalizedMessage());
621
        }
622
    }
623

    
624
    private static void getAllTexts(Locale lang, ClassLoader classLoader,
625
            Map texts) {
626
        for (Iterator iterator = resourceFamilies.iterator(); iterator
627
                .hasNext();) {
628
            String family = (String) iterator.next();
629
            if (classLoader == null) {
630
                addResourceFamily(family, lang, texts);
631
            }
632
            else {
633
                addResourceFamily(family, lang, texts, classLoader);
634
            }
635
        }
636
    }
637

    
638
    public static Map getAllTexts(Locale lang) {
639
        Map texts = new HashMap();
640
        getAllTexts(lang, null, texts);
641
        for (Iterator iterator = classLoaders.iterator(); iterator
642
                .hasNext();) {
643
            getAllTexts(lang, (ClassLoader) iterator.next(), texts);
644
        }
645
        return texts;
646
    }
647

    
648
    /**
649
     * Returns an ArrayList containing the ordered list of prefered Locales Each
650
     * element of the ArrayList is a Locale object.
651
     * 
652
     * @return an ArrayList containing the ordered list of prefered Locales Each
653
     *         element of the ArrayList is a Locale object.
654
     */
655
    public static ArrayList getPreferredLocales() {
656
        return preferredLocales;
657
    }
658

    
659
    /**
660
     * <p>
661
     * Sets the ordered list of preferred locales. Each element of the ArrayList
662
     * is a Locale object.
663
     * </p>
664
     * 
665
     * <p>
666
     * Note that calling this method does not load any translation, it just adds
667
     * the language to the preferred locales list, so this method must be always
668
     * called before the translations are loaded using the addResourceFamily()
669
     * methods.
670
     * </p>
671
     * 
672
     * <p>
673
     * It there was any language in the preferred locale list, the language and
674
     * its associated translations are deleted.
675
     * </p>
676
     * 
677
     * 
678
     * @param preferredLocales
679
     *            an ArrayList containing Locale objects. The ArrayList
680
     *            represents an ordered list of preferred locales
681
     */
682
    public static void setPreferredLocales(ArrayList preferredLocalesList) {
683
        // delete all existing locales
684
        Iterator oldLocales = preferredLocales.iterator();
685
        while (oldLocales.hasNext()) {
686
            removeLocale((Locale) oldLocales.next());
687
        }
688

    
689
        // add the new locales now
690
        for (int numLocale = 0; numLocale < preferredLocalesList.size(); numLocale++) {
691
            addLocale((Locale) preferredLocalesList.get(numLocale));
692
        }
693
    }
694

    
695
    /**
696
     * Adds a Locale at the end of the ordered list of preferred locales. Note
697
     * that calling this method does not load any translation, it just adds the
698
     * language to the preferred locales list, so this method must be always
699
     * called before the translations are loaded using the addResourceFamily()
700
     * methods.
701
     * 
702
     * @param lang
703
     *            A Locale object specifying the locale to add
704
     */
705
    public static void addLocale(Locale lang) {
706
        if (!preferredLocales.contains(lang)) { // avoid duplicates
707
            preferredLocales.add(lang); // add the lang to the ordered list of
708
            // preferred locales
709
            HashMap dict = new HashMap(_INITIALSIZE);
710
            localeResources.add(dict); // add a hashmap which will contain the
711
            // translation for this language
712
            addResourceFamily("org.gvsig.i18n.resources.translations.text",
713
                    lang, dict);
714
        }
715
    }
716

    
717
    /**
718
     * Removes the specified Locale from the list of preferred locales and the
719
     * translations associated with this locale.
720
     * 
721
     * @param lang
722
     *            A Locale object specifying the locale to remove
723
     * @return True if the locale was in the preferred locales list, false
724
     *         otherwise
725
     */
726
    public static boolean removeLocale(Locale lang) {
727
        int numLocale = preferredLocales.indexOf(lang);
728
        if (numLocale != -1) { // we found the locale in the list
729
            try {
730
                preferredLocales.remove(numLocale);
731
                localeResources.remove(numLocale);
732
            } catch (IndexOutOfBoundsException ex) {
733
                logger.warn(_CLASSNAME + "." + "removeLocale: "
734
                        + ex.getLocalizedMessage());
735
            }
736
            return true;
737
        }
738
        return false;
739
    }
740

    
741
    /**
742
     * Cleans the translation tables (removes all the translations from memory).
743
     */
744
    public static void removeResources() {
745
        for (int numLocale = 0; numLocale < localeResources.size(); numLocale++) {
746
            ((HashMap) localeResources.get(numLocale)).clear();
747
        }
748
    }
749

    
750
    /**
751
     * The number of translation keys which have been loaded till now (In other
752
     * words: the number of available translation strings).
753
     * 
754
     * @param lang
755
     *            The language for which we want to know the number of
756
     *            translation keys return The number of translation keys for the
757
     *            provided language.
758
     */
759
    protected static int size(Locale lang) {
760
        int numLocale = preferredLocales.indexOf(lang);
761
        if (numLocale != -1) {
762
            return ((HashMap) localeResources.get(numLocale)).size();
763
        }
764
        ;
765
        return 0;
766
    }
767

    
768
    /**
769
     * Checks if some locale has been added to the preferred locales list, which
770
     * is necessary before loading any translation because only the translations
771
     * for the preferred locales are loaded.
772
     * 
773
     * @return
774
     */
775
    public static boolean hasLocales() {
776
        return preferredLocales.size() > 0;
777
    }
778

    
779
    /**
780
     * Searches the subdirectories of the provided directory, finding all the
781
     * translation files, and constructing a list of available translations. It
782
     * reports different country codes or variants, if available. For example,
783
     * if there is an en_US translation and an en_GB translation, both locales
784
     * will be present in the Vector.
785
     * 
786
     * @return
787
     */
788

    
789
    /**
790
     * 
791
     * @return A Vector containing the available locales. Each element is a
792
     *         Locale object
793
     */
794
    /*
795
     * public static Vector getAvailableLocales() { return _availableLocales; }
796
     */
797

    
798
    /**
799
     * 
800
     * @return A Vector containing the available languages. Each element is an
801
     *         String object
802
     */
803
    /*
804
     * public static Vector getAvailableLanguages() { Vector availableLanguages
805
     * = new Vector(); Locale lang; Enumeration locales =
806
     * _availableLocales.elements(); while (locales.hasMoreElements()) { lang =
807
     * (Locale) locales.nextElement();
808
     * availableLanguages.add(lang.getLanguage()); } return availableLanguages;
809
     * }
810
     */
811
}