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

History | View | Annotate | Download (24.2 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.Locale;
39
import java.util.Properties;
40
import java.util.Set;
41

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

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

    
75
    /* Each entry will contain a hashmap with translations. Each hasmap
76
     * contains the translations for one language, indexed by the
77
     * translation key. The translations for language (i) in the preferred locales
78
     * list are contained in the position (i) of the localeResources list */
79
    private static ArrayList localeResources = new ArrayList();
80
        private static ArrayList preferredLocales = new ArrayList(); // contains the ordered list of prefered languages/locales (class Locale)
81

    
82

    
83
        /* Set of resource families and classloaders used to load i18n resources. */
84
        private static Set resourceFamilies = new HashSet();
85
        private static Set classLoaders = new HashSet();
86

    
87
        /*
88
         * The language considered the origin of translations, which will
89
         * (possibly) be stored in a property file without language suffix
90
         * (ie: text.properties instead of text_es.properties).
91
         */
92
        private static String baseLanguage = "es";
93
        private static Locale baseLocale = new Locale(baseLanguage);
94

    
95
        /**
96
         * <p>Gets the localized message associated with the provided key.
97
         * If the key is not in the dictionary, return the key and register
98
         * the failure in the log.</p>
99
         *
100
         * <p>The <code>callerName</code> parameter is only
101
         * used as a label when logging, so any String can be used. However, a
102
         * meaningful String should be used, such as the name of the class requiring
103
         * the translation services, in order to identify the source of the failure
104
         * in the log.</p>
105
         *
106
         * @param key         An String which identifies the translation that we want to get.
107
         * @param callerName  A symbolic name given to the caller of this method, to
108
         *                    show it in the log if the key was not found
109
         * @return            an String with the message associated with the provided key.
110
         *                    If the key is not in the dictionary, return the key. If the key
111
         *                    is null, return null.
112
         */
113
        public static String getText(String key, String callerName) {
114
                if (key==null) {
115
                        return null;
116
                }
117
                for (int numLocale=0; numLocale<localeResources.size(); numLocale++) {
118
                        // try to get the translation for any of the languagues in the preferred languages list
119
                        String translation = ((Properties)localeResources.get(numLocale)).getProperty(key);
120
                        if (translation!=null && !translation.equals("")) {
121
                                return translation;
122
                        }
123
                }
124
                logger.warn(callerName+ " -- Cannot find translation for "+key);
125
                return key;
126
        }
127

    
128
        public static String getText(String key,  String[] arguments, String callerName) {
129
                String translation = getText(key, callerName);
130
                if (translation!=null && arguments!=null ) {
131
                        try {
132
                                translation = MessageFormat.format(translation, arguments);
133
                        }
134
                        catch (IllegalFormatException ex) {
135
                                logger.error(callerName+" -- Error formating key: "+key+" -- "+translation);
136
                        }
137
                }
138
                return translation;
139
        }
140
        
141
        public static String translate(String message, String[] args) {
142
                String msg = message;
143
                if (msg == null) {
144
                        return "";
145
                }
146
                msg = getText(msg, args);
147
                if (msg == null) {
148
                        msg = "_" + message.replace("_", " ");
149
                }
150
                return msg;
151
        }
152

    
153
        public static String translate(String message) {
154
                String msg = message;
155
                if (msg == null) {
156
                        return "";
157
                }
158
                msg = getText(msg, (String[]) null);
159
                if (msg == null) {
160
                        msg = "_" + message.replace("_", " ");
161
                }
162
                return msg;
163
        }
164

    
165
        /**
166
         * <p>Gets the localized message associated with the provided key.
167
         * If the key is not in the dictionary or the translation is empty,
168
         * return the key and register the failure in the log.</p>
169
         *
170
         * @param key     An String which identifies the translation that we want to get.
171
         * @return        an String with the message associated with the provided key.
172
         *                If the key is not in the dictionary or the translation is empty,
173
         *                return the key. If the key is null, return null.
174
         */
175
        public static String getText(String key) {
176
                return getText(key, _CLASSNAME);
177
        }
178

    
179
        public static String getText(String key, String[] arguments) {
180
                return getText(key, arguments, _CLASSNAME);
181
        }
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
         * it returns null and the failure is only registered in the log if
188
         * the param log is true.</p>
189
         *
190
         * @param key        An String which identifies the translation that we want
191
         *                                 to get.
192
         * @param log        Determines whether log a key failure or not
193
         * @return                an String with the message associated with the provided key,
194
         *                                 or null if the key is not in the dictionary or the
195
         *                                 translation is empty.
196
         */
197
        public static String getText(String key, boolean log) {
198
                return getText(key, _CLASSNAME, log);
199
        }
200

    
201
        public static String getText(String key, String[] arguments, boolean log) {
202
                String translation = getText(key, _CLASSNAME, log);
203
                if (translation!=null && arguments!=null ) {
204
                        try {
205
                                translation = MessageFormat.format(translation, arguments);
206
                        } catch (IllegalFormatException ex) {
207
                                if (log) {
208
                                        logger.error(_CLASSNAME+" -- Error formating key: "+key+" -- "+translation);
209
                                }
210
                        }
211
                }
212
                return translation;
213
        }
214

    
215
        /**
216
         * <p>Gets the localized message associated with the provided key.
217
         * If the key is not in the dictionary, it returns null and the failure
218
         * is only registered in the log if the param log is true.</p>
219
         *
220
         * @param key         An String which identifies the translation that we want to get.
221
         * @param callerName  A symbolic name given to the caller of this method, to
222
         *                    show it in the log if the key was not found
223
         * @param log         Determines whether log a key failure or not
224
         * @return            an String with the message associated with the provided key,
225
         *                    or null if the key is not in the dictionary.
226
         */
227
        public static String getText(String key, String callerName, boolean log) {
228
                if (key==null) {
229
                        return null;
230
                }
231
                for (int numLocale=0; numLocale<localeResources.size(); numLocale++) {
232
                        // try to get the translation for any of the languagues in the preferred languages list
233
                        String translation = ((Properties)localeResources.get(numLocale)).getProperty(key);
234
                        if (translation!=null && !translation.equals("")) {
235
                                return translation;
236
                        }
237
                }
238
                if (log) {
239
                        logger.warn(callerName+" -- Cannot find translation for "+key);
240
                }
241
                return null;
242
        }
243

    
244
        public static String getText(String key, String[] arguments, String callerName, boolean log) {
245
                String translation = getText(key, callerName, log);
246
                if (translation!=null) {
247
                        try {
248
                                translation = MessageFormat.format(translation, arguments);
249
                        }
250
                        catch (IllegalFormatException ex) {
251
                                if (log) {
252
                                        logger.error(callerName+" -- Error formating key: "+key+" -- "+translation);
253
                                }
254
                        }
255
                }
256
                return translation;
257
        }
258

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

    
300
        /**
301
         * <p>Adds an additional family of resource files containing some translations.
302
         * The search path to locate the files is provided by the dirList parameter.</p>
303
         *
304
         * <p>See {@link addResourceFamily(String, ClassLoader)} for a discussion about the
305
         * format of the property files and the way to determine the candidat files
306
         * to load. Note that those methods are different in the way to locate the
307
         * candidat files. This method searches in the provided paths (<code>dirList</code>
308
         * parameter), while the referred method searches using the getResource method
309
         * of the provided ClassLoader.</p>
310
         *
311
         * @param family    The family name (or base name) which is used to search
312
         *                  actual properties files.
313
         * @param dirList   A list of search paths to locate the property files
314
         * @throws MalformedURLException
315
         * @see             <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/ResourceBundle.html">ResourceBundle</a>
316
         */
317
        public static void addResourceFamily(String family, File[] dirList) throws MalformedURLException{
318
                // use our own classloader
319
                URL[] urls = new URL[dirList.length];
320

    
321
                        int i;
322
                        for (i=0; i<urls.length; i++) {
323
                                urls[i] = dirList[i].toURL();
324
                        }
325

    
326
                ClassLoader loader = new MessagesClassLoader(urls);
327
                addResourceFamily(family, loader, "");
328
        }
329

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

    
355

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

    
383
                if (totalLocales == 0) {
384
                        // if it's empty, warn about that
385
                        logger.warn("There is not preferred languages list. Maybe the Messages class was not initialized");
386
                }
387

    
388
                resourceFamilies.add(family);
389
                classLoaders.add(loader);
390

    
391
                for (int numLocale=0; numLocale<totalLocales; numLocale++) { // for each language
392
                        properties =  new Properties();
393

    
394
                        lang = (Locale) preferredLocales.get(numLocale);
395
                        translations = (Properties) localeResources.get(numLocale);
396

    
397
                        addResourceFamily(lang, translations, family, loader, callerName);
398
                }
399
        }
400

    
401
        private static void addResourceFamily(Locale lang, Properties translations,
402
                        String family, ClassLoader loader, String callerName) {
403
                Properties properties = new Properties();
404
                String resource = family.replace('.', '/') + "_" + lang.toString()
405
                                + ".properties";
406
                InputStream is = loader.getResourceAsStream(resource);
407
                if (is != null) {
408
                        try {
409
                                properties.load(is);
410
                        } catch (IOException e) {
411
                        }
412
                } else if (lang.equals(baseLocale)) {
413
                        // try also "text.properties" for the base language
414
                        is = loader.getResourceAsStream(family.replace('.', '/')
415
                                        + ".properties");
416

    
417

    
418
                        if (is != null) {
419
                                try {
420
                                        properties.load(is);
421
                                } catch (IOException e) {
422
                                }
423
                        }
424

    
425
                }
426
                Enumeration keys = properties.keys();
427
                while (keys.hasMoreElements()) {
428
                        String currentKey = (String) keys.nextElement();
429
                        if (!translations.containsKey(currentKey)) {
430
                                translations
431
                                                .put(currentKey, properties.getProperty(currentKey));
432
                        }
433
                }
434

    
435
        }
436

    
437
        /**
438
         * <p>Adds an additional family of resource files containing some translations.</p>
439
         *
440
         * <p>This method is identical to {@link addResourceFamily(String, ClassLoader, String)},
441
         * except that it uses the caller's class loader.</p>
442
         *
443
         * <p>See {@link addResourceFamily(String, ClassLoader)} for a discussion about the
444
         * format of the property files and the way to determine the candidat files
445
         * to load.</p>
446
         *
447
         * @param family      The family name (or base name) which is used to search
448
         *                    actual properties files.
449
         * @param callerName  A symbolic name given to the caller of this method, to
450
         *                    show it in the log if there is an error. This is only used
451
         *                    to show
452
         *                    something meaningful in the log, so you can use any string
453
         * @see               <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/ResourceBundle.html">ResourceBundle</a>
454
         */
455
        public static void addResourceFamily(String family, String callerName) {
456
                addResourceFamily(family, Messages.class.getClassLoader(), callerName);
457
        }
458

    
459

    
460
        /**
461
         * Returns an ArrayList containing the ordered list of prefered Locales
462
         * Each element of the ArrayList is a Locale object.
463
         *
464
         * @return an ArrayList containing the ordered list of prefered Locales
465
         * Each element of the ArrayList is a Locale object.
466
         */
467
        public static ArrayList getPreferredLocales() {
468
                return preferredLocales;
469
        }
470

    
471
        /**
472
         * <p>Sets the ordered list of preferred locales.
473
         * Each element of the ArrayList is a Locale object.</p>
474
         *
475
         * <p>Note that calling this method does not load any translation, it just
476
         * adds the language to the preferred locales list, so this method must
477
         * be always called before the translations are loaded using
478
         * the addResourceFamily() methods.</p>
479
         *
480
         * <p>It there was any language in the preferred locale list, the language
481
         * and its associated translations are deleted.</p>
482
         *
483
         *
484
         * @param preferredLocales an ArrayList containing Locale objects.
485
         * The ArrayList represents an ordered list of preferred locales
486
         */
487
        public static void setPreferredLocales(ArrayList preferredLocalesList) {
488
                // delete all existing locales
489
                Iterator oldLocales = preferredLocales.iterator();
490
                while (oldLocales.hasNext()) {
491
                        removeLocale((Locale) oldLocales.next());
492
                }
493

    
494
                // add the new locales now
495
                for (int numLocale=0; numLocale < preferredLocalesList.size(); numLocale++) {
496
                        addLocale((Locale) preferredLocalesList.get(numLocale));
497
                }
498
        }
499

    
500
        /**
501
         * Adds a Locale at the end of the ordered list of preferred locales.
502
         * Note that calling this method does not load any translation, it just
503
         * adds the language to the preferred locales list, so this method must
504
         * be always called before the translations are loaded using
505
         * the addResourceFamily() methods.
506
         *
507
         * @param lang   A Locale object specifying the locale to add
508
         */
509
        public static void addLocale(Locale lang) {
510
                if (!preferredLocales.contains(lang)) { // avoid duplicates
511
                                preferredLocales.add(lang); // add the lang to the ordered list of preferred locales
512
                                Properties dict = new Properties();
513
                                localeResources.add(dict); // add a hashmap which will contain the translation for this language
514
                }
515
        }
516

    
517
        /**
518
         * Removes the specified Locale from the list of preferred locales and the
519
         * translations associated with this locale.
520
         *
521
         * @param lang   A Locale object specifying the locale to remove
522
         * @return       True if the locale was in the preferred locales list, false otherwise
523
         */
524
        public static boolean removeLocale(Locale lang) {
525
                int numLocale = preferredLocales.indexOf(lang);
526
                if (numLocale!=-1) { // we found the locale in the list
527
                        try {
528
                                preferredLocales.remove(numLocale);
529
                                localeResources.remove(numLocale);
530
                        }
531
                        catch (IndexOutOfBoundsException ex) {
532
                                logger.warn(_CLASSNAME + "." + "removeLocale: " + ex.getLocalizedMessage());
533
                        }
534
                        return true;
535
                }
536
                return false;
537
        }
538

    
539
        /**
540
         * Cleans the translation tables (removes all the translations from memory).
541
         */
542
        public static void removeResources() {
543
                for (int numLocale=0; numLocale<localeResources.size(); numLocale++) {
544
                        ((Properties)localeResources.get(numLocale)).clear();
545
                }
546
        }
547

    
548
        /**
549
         * The number of translation keys which have been loaded till now
550
         * (In other words: the number of available translation strings).
551
         *
552
         * @param lang The language for which we want to know the number of translation keys
553
         * return The number of translation keys for the provided language.
554
         */
555
        protected static int size(Locale lang) {
556
                int numLocale = preferredLocales.indexOf(lang);
557
                if (numLocale!=-1) {
558
                        return ((Properties)localeResources.get(numLocale)).size();
559
                };
560
                return 0;
561
        }
562

    
563
        protected static Set keySet(Locale lang) {
564
                int numLocale = preferredLocales.indexOf(lang);
565
                if (numLocale!=-1) {
566
                        return ((Properties)localeResources.get(numLocale)).keySet();
567
                } else {
568
                        return null;
569
                }
570
        }
571

    
572
        /**
573
         * Checks if some locale has been added to the preferred locales
574
         * list, which is necessary before loading any translation because
575
         * only the translations for the preferred locales are loaded.
576
         *
577
         * @return
578
         */
579
        public static boolean hasLocales() {
580
                return preferredLocales.size()>0;
581
        }
582

    
583
        /**
584
         * Gets the base language, the language considered the origin of
585
         * translations, which will be (possibly) stored in a property
586
         * file without language suffix
587
         * (ie: text.properties instead of text_es.properties).
588
         */
589
        public static String getBaseLanguage() {
590
                return baseLanguage;
591
        }
592

    
593
        /**
594
         * Sets the base language, the language considered the origin of
595
         * translations, which will be (possibly)
596
         * stored in a property file without language suffix
597
         * (ie: text.properties instead of text_es.properties).
598
         *
599
         * @param lang The base language to be set
600
         */
601
        public static void setBaseLanguage(String lang) {
602
                baseLanguage = lang;
603
                baseLocale = new Locale(baseLanguage);
604
        }
605

    
606
        /*
607
         * Searches the subdirectories of the provided directory, finding
608
         * all the translation files, and constructing a list of available translations.
609
         * It reports different country codes or variants, if available.
610
         * For example, if there is an en_US translation and an en_GB translation, both
611
         * locales will be present in the Vector.
612
         *
613
         * @return
614
         */
615

    
616
        /**
617
         *
618
         * @return A Vector containing the available locales. Each element is a Locale object
619
         */
620
        /*public static Vector getAvailableLocales() {
621
                return _availableLocales;
622
        }*/
623

    
624
        /**
625
         *
626
         * @return A Vector containing the available languages. Each element is an String object
627
         */
628
        /*public static Vector getAvailableLanguages() {
629
                Vector availableLanguages = new Vector();
630
                Locale lang;
631
                Enumeration locales = _availableLocales.elements();
632
                while (locales.hasMoreElements()) {
633
                        lang = (Locale) locales.nextElement();
634
                        availableLanguages.add(lang.getLanguage());
635
                }
636
                return availableLanguages;
637
        }*/
638

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

    
648
        private static void getAllTexts(Locale lang, ClassLoader classLoader,
649
                        Properties texts) {
650
                ClassLoader loader = classLoader == null ? Messages.class
651
                                .getClassLoader() : classLoader;
652

    
653
                for (Iterator iterator = resourceFamilies.iterator(); iterator
654
                                .hasNext();) {
655
                        String family = (String) iterator.next();
656
                        addResourceFamily(lang, texts, family, loader,
657
                                        "Messages.getAllTexts");
658
                }
659
        }
660

    
661
}