Revision 25959 branches/v10/libraries/libInternationalization/src/org/gvsig/i18n/Messages.java

View differences:

Messages.java
1 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
*/
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 41

  
42 42
package org.gvsig.i18n;
43 43

  
44 44
import java.io.File;
45 45
import java.net.MalformedURLException;
46 46
import java.net.URL;
47
import java.net.URLClassLoader;
48
import java.util.ArrayList;
49
import java.util.Enumeration;
50
import java.util.HashMap;
51
import java.util.Iterator;
52
import java.util.List;
53
import java.util.Locale;
54
import java.util.MissingResourceException;
55
import java.util.ResourceBundle;
56
import java.util.StringTokenizer;
47
import java.util.*;
57 48

  
58 49
import org.apache.log4j.Logger;
59 50

  
60 51
/**
61
 * <p>This class offers some methods to provide internationalization services
62
 * to other projects. All the methods are static.</p>
52
 * <p>
53
 * This class offers some methods to provide internationalization services to
54
 * other projects. All the methods are static.
55
 * </p>
63 56
 * 
64
 * <p>The most useful method is {@link #getText(String) getText(key)} (and family),
65
 * which returns the translation associated
66
 * with the provided key. The class must be initialized before getText can be
67
 * used.</p>
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>
68 62
 * 
69
 * <p>The typical usage sequence would be:</p>
63
 * <p>
64
 * The typical usage sequence would be:
65
 * </p>
70 66
 * <ul>
71
 * <li>Add some locale to the prefered locales list: <code>Messages.addLocale(new Locale("es"))</code></li>
72
 * <li>Add some resource file containing translations: <code>Messages.addResourceFamily("text", new File("."))</code></li>
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>
73 71
 * <li>And finaly getText can be used: <code>Messages.getText("aceptar")</code></li>
74 72
 * </ul>
75 73
 * 
76
 * <p>The resource files are Java properties files, which contain <code>key=translation</code>
77
 * pairs, one
78
 * pair per line. These files must be encoded using iso-8859-1 encoding, and unicode escaped
79
 * sequences must be used to include characters outside the former encoding.
80
 * There are several ways to specify the property file to load, see the different
81
 * addResourceFamily methods for details.</p> 
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 82
 * 
83 83
 * @author Cesar Martinez Izquierdo (cesar.martinez@iver.es)
84
 *
84
 * 
85 85
 */
86 86
public class Messages {
87 87
    private static Logger logger = Logger.getLogger("Messages");
88 88
    private static String _CLASSNAME = "org.gvsig.i18n.Messages";
89
    private static int _INITIALSIZE = 3700; // I try to set an initial capacity similar to the amount of strings in gvSIG
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();
90 104
    
91
    /* Each entry will contain a hashmap with translations. Each hasmap
92
     * contains the translations for one language, indexed by the
93
     * translation key. The translations for language (i) in the preferred locales
94
     * list are contained in the position (i) of the localeResources list */
95
    private static ArrayList localeResources = new ArrayList(); 
96
	private static ArrayList preferredLocales = new ArrayList(); // contains the ordered list of prefered languages/locales (class Locale)
97
	
98
	/**
99
	 * <p>Gets the localized message associated with the provided key.
100
	 * If the key is not in the dictionary, return the key and register
101
	 * the failure in the log.</p>
102
	 * 
103
	 * <p>The <code>callerName</code> parameter is only
104
	 * used as a label when logging, so any String can be used. However, a
105
	 * meaningful String should be used, such as the name of the class requiring
106
	 * the translation services, in order to identify the source of the failure
107
	 * in the log.</p>    
108
	 * 
109
	 * @param key         An String which identifies the translation that we want to get. 
110
	 * @param callerName  A symbolic name given to the caller of this method, to
111
	 *                    show it in the log if the key was not found
112
	 * @return            an String with the message associated with the provided key.
113
	 *                    If the key is not in the dictionary, return the key. If the key
114
	 *                    is null, return null.
115
	 */
116
	public static String getText(String key, String callerName) {
117
		if (key==null) return null;
118
		for (int numLocale=0; numLocale<localeResources.size(); numLocale++) {
119
			// try to get the translation for any of the languagues in the preferred languages list
120
			String translation = (String) ((HashMap)localeResources.get(numLocale)).get(key);
121
			if (translation!=null && !translation.equals(""))
122
				return translation;
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;
123 218
		}
124
		logger.warn(callerName+ " -- " +Messages.getText("No_se_encontro_la_traduccion_para", _CLASSNAME ,false)+" "+key);
125
		return key;
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
	    }
126 226
	}
127
	
128
	/**
129
	 * <p>Gets the localized message associated with the provided key.
130
	 * If the key is not in the dictionary or the translation is empty,
131
	 * return the key and register the failure in the log.</p>
132
	 * 
133
	 * @param key     An String which identifies the translation that we want to get.
134
	 * @return        an String with the message associated with the provided key.
135
	 *                If the key is not in the dictionary or the translation is empty,
136
	 *                return the key. If the key is null, return null.
137
	 */
138
	public static String getText(String key) {
139
		return getText(key, _CLASSNAME);
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();
140 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) {
141 392
	
142
	/**
143
	 * <p>Gets the localized message associated with the provided key.
144
	 * If the key is not in the dictionary or the translation is empty,
145
	 * it returns null and the failure is only registered in the log if
146
	 * the param log is true.</p>
147
	 * 
148
	 * @param key	An String which identifies the translation that we want
149
	 * 				to get.
150
	 * @param log	Determines whether log a key failure or not
151
	 * @return		an String with the message associated with the provided key,
152
	 * 				or null if the key is not in the dictionary or the
153
	 * 				translation is empty.
154
	 */
155
	public static String getText(String key, boolean log) {
156
		return getText(key, _CLASSNAME, log);
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));
157 408
	}
158
	
159
	/**
160
	 * <p>Gets the localized message associated with the provided key.
161
	 * If the key is not in the dictionary, it returns null and the failure
162
	 * is only registered in the log if the param log is true.</p>
163
	 * 
164
	 * @param key         An String which identifies the translation that we want to get.
165
	 * @param callerName  A symbolic name given to the caller of this method, to
166
	 *                    show it in the log if the key was not found
167
	 * @param log         Determines whether log a key failure or not
168
	 * @return            an String with the message associated with the provided key,
169
	 *                    or null if the key is not in the dictionary.
170
	 */
171
	public static String getText(String key, String callerName, boolean log) {
172
		if (key==null) return null;
173
		for (int numLocale=0; numLocale<localeResources.size(); numLocale++) {
174
			// try to get the translation for any of the languagues in the preferred languages list
175
			String translation = (String) ((HashMap)localeResources.get(numLocale)).get(key);
176
			if (translation!=null && !translation.equals(""))
177
				return translation;
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
		    }
178 441
		}
179
		if (log) {
180
			logger.warn(callerName+" -- "+Messages.getText("No_se_encontro_la_traduccion_para", _CLASSNAME ,false)+" "+key);
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
		    }
181 451
		}
182
		return null;
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
	    }
183 458
	}
459
    }
184 460

  
185
	/**
186
	 * <p>Adds an additional family of resource files containing some translations.
187
	 * A family is a group of files with a common baseName.
188
	 * The file must be an iso-8859-1 encoded file, which can contain any unicode
189
	 * character using unicode escaped sequences, and following the syntax:
190
	 * <code>key1=value1
191
	 * key2=value2</code>
192
	 * where 'key1' is the key used to identify the string and must not
193
	 * contain the '=' symbol, and 'value1' is the associated translation.</p>
194
	 * <p<For example:</p>
195
	 * <code>cancel=Cancelar
196
	 * accept=Aceptar</code>
197
	 * <p>Only one pair key-value is allowed per line.</p>
198
	 * 
199
	 * <p>The actual name of the resource file to load is determined using the rules
200
	 * explained in the class java.util.ResourceBundle. Summarizing, for each language
201
	 * in the specified preferred locales list it will try to load a file with
202
	 *  the following structure: <code>family_locale.properties</code></p>
203
	 *
204
	 * <p>For example, if the preferred locales list contains {"fr", "es", "en"}, and
205
	 * the family name is "text", it will try to load the files "text_fr.properties",
206
	 * "text_es.properties" and finally "text_en.properties".</p>
207
	 * 
208
	 * <p>Locales might be more specific, such us "es_AR"  (meaning Spanish from Argentina)
209
	 * or "es_AR_linux" (meaning Linux system preferring Spanish from Argentina). In the
210
	 * later case, it will try to load "text_es_AR_linux.properties", then
211
	 * "text_es_AR.properties" if the former fails, and finally "text_es.properties".</p>
212
	 * 
213
	 * <p>The directory used to locate the resource file is determining by using the
214
	 * getResource method from the provided ClassLoader.</p>
215
	 *  
216
	 * @param family    The family name (or base name) which is used to search
217
	 *                  actual properties files.
218
	 * @param loader    A ClassLoader which is able to find a property file matching
219
	 * 					the specified family name and the preferred locales
220
	 * @see             <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/ResourceBundle.html">ResourceBundle</a>
221
	 */
222
	public static void addResourceFamily(String family, ClassLoader loader) {
223
		addResourceFamily(family, loader, "");
224
	}
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) {
225 488
	
226
	/**
227
	 * <p>Adds an additional family of resource files containing some translations.
228
	 * The search path to locate the files is provided by the dirList parameter.</p>
229
	 * 
230
	 * <p>See {@link addResourceFamily(String, ClassLoader)} for a discussion about the
231
	 * format of the property files and the way to determine the candidat files
232
	 * to load. Note that those methods are different in the way to locate the 
233
	 * candidat files. This method searches in the provided paths (<code>dirList</code>
234
	 * parameter), while the referred method searches using the getResource method
235
	 * of the provided ClassLoader.</p>
236
	 *  
237
	 * @param family    The family name (or base name) which is used to search
238
	 *                  actual properties files.
239
	 * @param dirList   A list of search paths to locate the property files
240
	 * @throws MalformedURLException
241
	 * @see             <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/ResourceBundle.html">ResourceBundle</a>
242
	 */
243
	public static void addResourceFamily(String family, File[] dirList) throws MalformedURLException{
244
		// use our own classloader
245
		URL[] urls = new URL[dirList.length];		
246
	
247
			int i;
248
			for (i=0; i<urls.length; i++) {
249
				urls[i] = dirList[i].toURL();
250
			}
251
	
252
		ClassLoader loader = new MessagesClassLoader(urls);
253
		addResourceFamily(family, loader, "");
254
	}
489
	resourceFamilies.add(family);
255 490

  
256
	/**
257
	 * <p>Adds an additional family of resource files containing some translations.
258
	 * The search path to locate the files is provided by the dir parameter.</p>
259
	 * 
260
	 * <p>See {@link addResourceFamily(String, ClassLoader)} for a discussion about the
261
	 * format of the property files and the way to determine the candidat files
262
	 * to load. Note that those methods are different in the way to locate the 
263
	 * candidat files. This method searches in the provided path (<code>dir</code>
264
	 * parameter), while the referred method searches using the getResource method
265
	 * of the provided ClassLoader.</p>
266
	 *  
267
	 * @param family    The family name (or base name) which is used to search
268
	 *                  actual properties files.
269
	 * @param dir       The search path to locate the property files
270
	 * @throws MalformedURLException
271
	 * @see             <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/ResourceBundle.html">ResourceBundle</a>
272
	 */
273
	public static void addResourceFamily(String family, File dir) throws MalformedURLException{
274
		// use our own classloader
275
		URL[] urls = new URL[1];		
276
		urls[0] = dir.toURL();
277
		ClassLoader loader = new MessagesClassLoader(urls);
278
		addResourceFamily(family, loader, "");
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));
279 503
	}
504
	Locale lang;
505
	ResourceBundle bundle = null;
280 506

  
281

  
282
	/**
283
	 * <p>Adds an additional family of resource files containing some translations.
284
	 * The search path is determined by the getResource method from the
285
	 * provided ClassLoader.</p>
286
	 * 
287
	 * <p>This method is identical to {@link addResourceFamily(String, ClassLoader)},
288
	 * except that it adds a <pode>callerName</code> parameter to show in the log.</p>
289
	 * 
290
	 * <p>See {@link addResourceFamily(String, ClassLoader)} for a discussion about the
291
	 * format of the property files andthe way to determine the candidat files
292
	 * to load.</p>
293
	 *  
294
	 * @param family      The family name (or base name) which is used to search
295
	 *                    actual properties files.
296
	 * @param loader      A ClassLoader which is able to find a property file matching
297
	 * 					  the specified family name and the preferred locales
298
	 * @param callerName  A symbolic name given to the caller of this method, to
299
	 *                    show it in the log if there is an error
300
	 * @see               <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/ResourceBundle.html">ResourceBundle</a>
301
	 */
302
	public static void addResourceFamily(String family, ClassLoader loader, String callerName) {
303
		String currentKey;
304
		Enumeration properties;
305
		Locale defaultLocale = Locale.getDefault();
306
		int totalLocales = preferredLocales.size();
307

  
308
		if (totalLocales == 0) {
309
			// if it's empty, warn about that
310
			logger.warn(Messages.getText("No_hay_lista_de_idiomas_preferidos_quiza_olvido_inicializar_clase", _CLASSNAME, false));
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
		    }
311 526
		}
312
		Locale lang;		
313
		ResourceBundle bundle = null;
314
		
315
		for (int numLocale=0; numLocale<totalLocales; numLocale++) { // for each language
316
			lang = (Locale) preferredLocales.get(numLocale);
317
			try {
318
				/**
319
				 * Here we are doing a pervert use of getBundle method. Normally, it is call it in in this way:
320
				 * getBundle("text", "en", loader), in order to get the file 'text_en.properties'. However, in
321
				 * this way the default file ('text.properties') is also loaded, and we want to avoid this
322
				 * (because 'text.properties contains the Spanish translation).
323
				 * So we use the method in this non-standard way:
324
				 * getBundle("text_en", "en", loader), ensuring that we are just loading the file 'text_en.properties'
325
				 **/
326
				if (!lang.getLanguage().equals("es"))
327
					bundle = ResourceBundle.getBundle(family+"_"+lang.getLanguage(), lang, loader);
328
				else { // we allow 'text.properties' to be loaded for Spanish
329
					 if (Locale.getDefault().equals(lang)) {
330
						 bundle = ResourceBundle.getBundle(family, lang, loader);						 
331
					 }
332
					 else {
333
						// we need to temporarily change the default locale to properly load spanish in corner cases
334
						 Locale.setDefault(lang);
335
						 bundle = ResourceBundle.getBundle(family, lang, loader);
336
						 Locale.setDefault(defaultLocale);
337
					 }					
338
				}
339 527

  
340
				properties = bundle.getKeys();
341
				while (properties.hasMoreElements()) {
342
					currentKey = (String) properties.nextElement();
343
					if (! ((HashMap)localeResources.get(numLocale)).containsKey(currentKey)) {
344
						((HashMap)localeResources.get(numLocale)).put(currentKey, bundle.getString(currentKey));
345
					}
346
				}
347
			}
348
			catch (MissingResourceException ex) {
349
				Locale.setDefault(defaultLocale);
350
				logger.warn(Messages.getText("Las_traducciones_no_pudieron_ser_cargadas", false)+" -- "+callerName+" -- "+lang.getLanguage());
351
			}
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
		    }
352 536
		}
537
	    } catch (MissingResourceException ex) {
538
		Locale.setDefault(defaultLocale);
539
		logger.warn(callerName + " " + ex.getLocalizedMessage());
540
	    }
353 541
	}
542
    }
543

  
544
    private static void addResourceFamily(String family, Locale lang, Map dict) {
354 545
	
355
	/**
356
	 * <p>Adds an additional family of resource files containing some translations.</p>
357
	 * 
358
	 * <p>This method is identical to {@link addResourceFamily(String, ClassLoader, String)},
359
	 * except that it uses the caller's class loader.</p>
360
	 * 
361
	 * <p>See {@link addResourceFamily(String, ClassLoader)} for a discussion about the
362
	 * format of the property files and the way to determine the candidat files
363
	 * to load.</p>
364
	 *  
365
	 * @param family      The family name (or base name) which is used to search
366
	 *                    actual properties files.
367
	 * @param callerName  A symbolic name given to the caller of this method, to
368
	 *                    show it in the log if there is an error. This is only used
369
	 *                    to show
370
	 *                    something meaningful in the log, so you can use any string
371
	 * @see               <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/ResourceBundle.html">ResourceBundle</a>
372
	 */
373
	public static void addResourceFamily(String family, String callerName) {
374
		String currentKey;
375
		Enumeration properties;
376
		Locale defaultLocale = Locale.getDefault();
377
		int totalLocales = preferredLocales.size();
546
	resourceFamilies.add(family);
378 547

  
379
		if (totalLocales == 0) {
380
			// if it's empty, warn about that
381
			logger.warn(Messages.getText("No_hay_lista_de_idiomas_preferidos_quiza_olvido_inicializar_clase", _CLASSNAME, false));
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);
382 568
		}
383
		Locale lang;		
384
		ResourceBundle bundle = null;
385
		
386
		for (int numLocale=0; numLocale<totalLocales; numLocale++) { // for each language
387
			try {
388
				lang = (Locale) preferredLocales.get(numLocale);
389
				if (!lang.getLanguage().equals("es")) 	// ensure we don't get a fallback
390
					bundle = ResourceBundle.getBundle(family+"_"+lang.getLanguage(), lang);
391
				else { // we allow 'text.properties' to be loaded for Spanish
392
					 if (Locale.getDefault().equals(lang)) {
393
						 bundle = ResourceBundle.getBundle(family, lang);						 
394
					 }
395
					 else {
396
						// we need to temporarily change the default locale to properly load spanish in corner cases
397
						 Locale.setDefault(lang);
398
						 bundle = ResourceBundle.getBundle(family, lang);
399
						 Locale.setDefault(defaultLocale);
400
					 }					
401
				}
402
	
403
				properties = bundle.getKeys();
404
				while (properties.hasMoreElements()) {
405
					currentKey = (String) properties.nextElement();
406
					if (! ((HashMap)localeResources.get(numLocale)).containsKey(currentKey)) {
407
						((HashMap)localeResources.get(numLocale)).put(currentKey, bundle.getString(currentKey));
408
					}
409
				}
410
			}
411
			catch (MissingResourceException ex) {
412
				Locale.setDefault(defaultLocale);
413
				logger.warn(callerName+" " + ex.getLocalizedMessage());
414
			}
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));
415 575
		}
576
	    }
577
	} catch (MissingResourceException ex) {
578
	    Locale.setDefault(defaultLocale);
579
	    logger.warn(Messages.class.getName() + " "
580
		    + ex.getLocalizedMessage());
416 581
	}
582
    }
583

  
584
    private static void addResourceFamily(String family, Locale lang, Map dict, ClassLoader classLoader) {
417 585
	
418
	private static void addResourceFamily(String family, Locale lang, HashMap dict) {
419
		String currentKey;
420
		Enumeration properties;
586
	resourceFamilies.add(family);
421 587

  
422
		ResourceBundle bundle = null;
423
		Locale defaultLocale = Locale.getDefault();
588
	String currentKey;
589
	Enumeration properties;
424 590

  
425
		try {
426
			if (!lang.getLanguage().equals("es")) // ensure we don't get a fallback
427
				bundle = ResourceBundle.getBundle(family+"_"+lang.getLanguage(), lang);
428
			else { // we allow 'text.properties' to be loaded for Spanish
429
				 if (Locale.getDefault().equals(lang)) {
430
					 bundle = ResourceBundle.getBundle(family, lang);						 
431
				 }
432
				 else {
433
					// we need to temporarily change the default locale to properly load spanish in corner cases
434
					 Locale.setDefault(lang);
435
					 bundle = ResourceBundle.getBundle(family, lang);
436
					 Locale.setDefault(defaultLocale);
437
				 }					
438
			}
439
			properties = bundle.getKeys();
440
			while (properties.hasMoreElements()) {
441
				currentKey = (String) properties.nextElement();
442
				if (! dict.containsKey(currentKey)) {
443
					dict.put(currentKey, bundle.getString(currentKey));
444
				}
445
			}
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);
446 608
		}
447
		catch (MissingResourceException ex) {
448
			Locale.setDefault(defaultLocale);
449
			logger.warn(Messages.class.getName()+" " + ex.getLocalizedMessage());
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));
450 615
		}
616
	    }
617
	} catch (MissingResourceException ex) {
618
	    Locale.setDefault(defaultLocale);
619
	    logger.warn(Messages.class.getName() + " "
620
		    + ex.getLocalizedMessage());
451 621
	}
622
    }
452 623

  
453
	
454
	/**
455
	 * Returns an ArrayList containing the ordered list of prefered Locales
456
	 * Each element of the ArrayList is a Locale object.
457
	 * 
458
	 * @return an ArrayList containing the ordered list of prefered Locales
459
	 * Each element of the ArrayList is a Locale object.
460
	 */
461
	public static ArrayList getPreferredLocales() {
462
		return preferredLocales;
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
	    }
463 635
	}
464
	
465
	/**
466
	 * <p>Sets the ordered list of preferred locales.
467
	 * Each element of the ArrayList is a Locale object.</p>
468
	 * 
469
	 * <p>Note that calling this method does not load any translation, it just
470
	 * adds the language to the preferred locales list, so this method must
471
	 * be always called before the translations are loaded using
472
	 * the addResourceFamily() methods.</p>
473
	 * 
474
	 * <p>It there was any language in the preferred locale list, the language
475
	 * and its associated translations are deleted.</p>
476
	 * 
477
	 * 
478
	 * @param preferredLocales an ArrayList containing Locale objects.
479
	 * The ArrayList represents an ordered list of preferred locales
480
	 */
481
	public static void setPreferredLocales(ArrayList preferredLocalesList) {
482
		// delete all existing locales
483
		Iterator oldLocales = preferredLocales.iterator();
484
		while (oldLocales.hasNext()) {
485
			removeLocale((Locale) oldLocales.next());
486
		}
487
		
488
		// add the new locales now
489
		for (int numLocale=0; numLocale < preferredLocalesList.size(); numLocale++) {
490
			addLocale((Locale) preferredLocalesList.get(numLocale));
491
		}
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);
492 644
	}
645
	return texts;
646
    }
493 647

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

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

  
534
	/**
535
	 * Cleans the translation tables (removes all the translations from memory).
536
	 */
537
	public static void removeResources() {
538
		for (int numLocale=0; numLocale<localeResources.size(); numLocale++) {
539
			((HashMap)localeResources.get(numLocale)).clear();
540
		}
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);
541 714
	}
715
    }
542 716

  
543
	/**
544
	 * The number of translation keys which have been loaded till now  
545
	 * (In other words: the number of available translation strings).
546
	 * 
547
	 * @param lang The language for which we want to know the number of translation keys
548
	 * return The number of translation keys for the provided language.
549
	 */
550
	protected static int size(Locale lang) {
551
		int numLocale = preferredLocales.indexOf(lang);
552
		if (numLocale!=-1) {
553
			return ((HashMap)localeResources.get(numLocale)).size();
554
		};
555
		return 0;
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;
556 737
	}
557
	
558
	/**
559
	 * Checks if some locale has been added to the preferred locales
560
	 * list, which is necessary before loading any translation because
561
	 * only the translations for the preferred locales are loaded.
562
	 * 
563
	 * @return
564
	 */
565
	public static boolean hasLocales() {
566
		return preferredLocales.size()>0;
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();
567 747
	}
568
	
569
	/**
570
	 * Searches the subdirectories of the provided directory, finding
571
	 * all the translation files, and constructing a list of available translations.
572
	 * It reports different country codes or variants, if available.
573
	 * For example, if there is an en_US translation and an en_GB translation, both
574
	 * locales will be present in the Vector.
575
	 * 
576
	 * @return
577
	 */
578
	
579
	/**
580
	 * 
581
	 * @return A Vector containing the available locales. Each element is a Locale object
582
	 */
583
	/*public static Vector getAvailableLocales() {
584
		return _availableLocales;
585
	}*/
586
	
587
	/**
588
	 * 
589
	 * @return A Vector containing the available languages. Each element is an String object
590
	 */
591
	/*public static Vector getAvailableLanguages() {
592
		Vector availableLanguages = new Vector();
593
		Locale lang;
594
		Enumeration locales = _availableLocales.elements();
595
		while (locales.hasMoreElements()) {
596
			lang = (Locale) locales.nextElement();
597
			availableLanguages.add(lang.getLanguage());
598
		}
599
		return availableLanguages;
600
	}*/
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
     */
601 811
}

Also available in: Unified diff