Statistics
| Revision:

svn-gvsig-desktop / branches / v2_0_0_prep / libraries / libInternationalization / src / org / gvsig / i18n / Messages.java @ 28469

History | View | Annotate | Download (24 KB)

1
/* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
2
*
3
* Copyright (C) 2006-2007 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.io.IOException;
46
import java.io.InputStream;
47
import java.net.MalformedURLException;
48
import java.net.URL;
49
import java.text.MessageFormat;
50
import java.util.ArrayList;
51
import java.util.Enumeration;
52
import java.util.HashSet;
53
import java.util.IllegalFormatException;
54
import java.util.Iterator;
55
import java.util.Locale;
56
import java.util.Properties;
57
import java.util.Set;
58

    
59
import org.slf4j.Logger;
60
import org.slf4j.LoggerFactory;
61

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

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

    
99

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

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

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

    
145
        public static String getText(String key,  String[] arguments, String callerName) {
146
                String translation = getText(key, callerName);
147
                if (translation!=null) {
148
                        try {
149
                                translation = MessageFormat.format(translation, arguments);
150
                        }
151
                        catch (IllegalFormatException ex) {
152
                                logger.error(callerName+" -- Error formating key: "+key+" -- "+translation);
153
                        }
154
                }
155
                return translation;
156
        }
157

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

    
172
        public static String getText(String key, String[] arguments) {
173
                return getText(key, arguments, _CLASSNAME);
174
        }
175

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

    
193
        public static String getText(String key, String[] arguments, boolean log) {
194
                String translation = getText(key, _CLASSNAME, log);
195
                if (translation!=null) {
196
                        try {
197
                                translation = MessageFormat.format(translation, arguments);
198
                        }
199
                        catch (IllegalFormatException ex) {
200
                                if (log) {
201
                                        logger.error(_CLASSNAME+" -- Error formating key: "+key+" -- "+translation);
202
                                }
203
                        }
204
                }
205
                return translation;
206
        }
207

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

    
237
        public static String getText(String key, String[] arguments, String callerName, boolean log) {
238
                String translation = getText(key, callerName, log);
239
                if (translation!=null) {
240
                        try {
241
                                translation = MessageFormat.format(translation, arguments);
242
                        }
243
                        catch (IllegalFormatException ex) {
244
                                if (log) {
245
                                        logger.error(callerName+" -- Error formating key: "+key+" -- "+translation);
246
                                }
247
                        }
248
                }
249
                return translation;
250
        }
251

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

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

    
314
                        int i;
315
                        for (i=0; i<urls.length; i++) {
316
                                urls[i] = dirList[i].toURL();
317
                        }
318

    
319
                ClassLoader loader = new MessagesClassLoader(urls);
320
                addResourceFamily(family, loader, "");
321
        }
322

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

    
348

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

    
376
                if (totalLocales == 0) {
377
                        // if it's empty, warn about that
378
                        logger.warn("There is not preferred languages list. Maybe the Messages class was not initialized");
379
                }
380

    
381
                resourceFamilies.add(family);
382
                classLoaders.add(loader);
383

    
384
                for (int numLocale=0; numLocale<totalLocales; numLocale++) { // for each language
385
                        properties =  new Properties();
386

    
387
                        lang = (Locale) preferredLocales.get(numLocale);
388
                        translations = (Properties) localeResources.get(numLocale);
389

    
390
                        addResourceFamily(lang, translations, family, loader, callerName);
391
                }
392
        }
393

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

    
410

    
411
                        if (is != null) {
412
                                try {
413
                                        properties.load(is);
414
                                } catch (IOException e) {
415
                                }
416
                        }
417

    
418
                }
419
                Enumeration keys = properties.keys();
420
                while (keys.hasMoreElements()) {
421
                        String currentKey = (String) keys.nextElement();
422
                        if (!translations.containsKey(currentKey)) {
423
                                translations
424
                                                .put(currentKey, properties.getProperty(currentKey));
425
                        }
426
                }
427

    
428
        }
429

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

    
452

    
453
        /**
454
         * Returns an ArrayList containing the ordered list of prefered Locales
455
         * Each element of the ArrayList is a Locale object.
456
         *
457
         * @return an ArrayList containing the ordered list of prefered Locales
458
         * Each element of the ArrayList is a Locale object.
459
         */
460
        public static ArrayList getPreferredLocales() {
461
                return preferredLocales;
462
        }
463

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

    
487
                // add the new locales now
488
                for (int numLocale=0; numLocale < preferredLocalesList.size(); numLocale++) {
489
                        addLocale((Locale) preferredLocalesList.get(numLocale));
490
                }
491
        }
492

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

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

    
532
        /**
533
         * Cleans the translation tables (removes all the translations from memory).
534
         */
535
        public static void removeResources() {
536
                for (int numLocale=0; numLocale<localeResources.size(); numLocale++) {
537
                        ((Properties)localeResources.get(numLocale)).clear();
538
                }
539
        }
540

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

    
556
        protected static Set keySet(Locale lang) {
557
                int numLocale = preferredLocales.indexOf(lang);
558
                if (numLocale!=-1) {
559
                        return ((Properties)localeResources.get(numLocale)).keySet();
560
                } else {
561
                        return null;
562
                }
563
        }
564

    
565
        /**
566
         * Checks if some locale has been added to the preferred locales
567
         * list, which is necessary before loading any translation because
568
         * only the translations for the preferred locales are loaded.
569
         *
570
         * @return
571
         */
572
        public static boolean hasLocales() {
573
                return preferredLocales.size()>0;
574
        }
575

    
576
        /**
577
         * Gets the base language, the language considered the origin of
578
         * translations, which will be (possibly) stored in a property
579
         * file without language suffix
580
         * (ie: text.properties instead of text_es.properties).
581
         */
582
        public static String getBaseLanguage() {
583
                return baseLanguage;
584
        }
585

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

    
599
        /*
600
         * Searches the subdirectories of the provided directory, finding
601
         * all the translation files, and constructing a list of available translations.
602
         * It reports different country codes or variants, if available.
603
         * For example, if there is an en_US translation and an en_GB translation, both
604
         * locales will be present in the Vector.
605
         *
606
         * @return
607
         */
608

    
609
        /**
610
         *
611
         * @return A Vector containing the available locales. Each element is a Locale object
612
         */
613
        /*public static Vector getAvailableLocales() {
614
                return _availableLocales;
615
        }*/
616

    
617
        /**
618
         *
619
         * @return A Vector containing the available languages. Each element is an String object
620
         */
621
        /*public static Vector getAvailableLanguages() {
622
                Vector availableLanguages = new Vector();
623
                Locale lang;
624
                Enumeration locales = _availableLocales.elements();
625
                while (locales.hasMoreElements()) {
626
                        lang = (Locale) locales.nextElement();
627
                        availableLanguages.add(lang.getLanguage());
628
                }
629
                return availableLanguages;
630
        }*/
631

    
632
        public static Properties getAllTexts(Locale lang) {
633
                Properties texts = new Properties();
634
                getAllTexts(lang, null, texts);
635
                for (Iterator iterator = classLoaders.iterator(); iterator.hasNext();) {
636
                        getAllTexts(lang, (ClassLoader) iterator.next(), texts);
637
                }
638
                return texts;
639
        }
640

    
641
        private static void getAllTexts(Locale lang, ClassLoader classLoader,
642
                        Properties texts) {
643
                ClassLoader loader = classLoader == null ? Messages.class
644
                                .getClassLoader() : classLoader;
645

    
646
                for (Iterator iterator = resourceFamilies.iterator(); iterator
647
                                .hasNext();) {
648
                        String family = (String) iterator.next();
649
                        addResourceFamily(lang, texts, family, loader,
650
                                        "Messages.getAllTexts");
651
                }
652
        }
653

    
654
}