25 |
* |
* |
26 |
* Contributors: |
* Contributors: |
27 |
* Martin O. J. Schmitz - initial API and implementation |
* Martin O. J. Schmitz - initial API and implementation |
28 |
* Stefan A. Krüger - additional utility classes |
* Stefan A. Tzeggai - additional utility classes |
29 |
******************************************************************************/ |
******************************************************************************/ |
30 |
package skrueger.i8n; |
package skrueger.i8n; |
31 |
|
|
32 |
|
import java.awt.event.ActionEvent; |
33 |
|
import java.awt.event.ActionListener; |
34 |
import java.beans.PropertyChangeEvent; |
import java.beans.PropertyChangeEvent; |
35 |
import java.beans.PropertyChangeListener; |
import java.beans.PropertyChangeListener; |
36 |
|
import java.io.Serializable; |
37 |
import java.util.ArrayList; |
import java.util.ArrayList; |
38 |
import java.util.HashMap; |
import java.util.HashMap; |
39 |
|
import java.util.Iterator; |
40 |
import java.util.List; |
import java.util.List; |
41 |
import java.util.Locale; |
import java.util.Locale; |
42 |
|
import java.util.Random; |
43 |
|
import java.util.Set; |
44 |
|
import java.util.WeakHashMap; |
45 |
|
|
46 |
import javax.swing.JComponent; |
import javax.swing.JComponent; |
47 |
|
|
48 |
import org.apache.log4j.Logger; |
import org.apache.log4j.Logger; |
49 |
|
import org.geotools.util.WeakHashSet; |
50 |
|
import org.opengis.util.InternationalString; |
51 |
|
|
52 |
|
import schmitzm.lang.ResourceProvider; |
53 |
|
import schmitzm.lang.SortableVector; |
54 |
|
import skrueger.geotools.Copyable; |
55 |
|
|
56 |
/** |
/** |
57 |
* Represents a {@link HashMap} of translations. toString() returns the |
* Represents a {@link HashMap} of translations. toString() returns the |
58 |
* appropriate translation |
* appropriate translation |
59 |
* |
* |
60 |
* @author @author <a href="mailto:[email protected]">Stefan Alfons |
* @author @author <a href="mailto:[email protected]">Stefan Alfons |
61 |
* Krüger</a> |
* Tzeggai</a> |
62 |
*/ |
*/ |
63 |
|
|
64 |
public class Translation extends HashMap<String, String> { |
public class Translation extends HashMap<String, String> implements |
65 |
|
Copyable<Translation>, Serializable { |
66 |
|
|
67 |
|
private static final long serialVersionUID = -347702744122305245L; |
68 |
|
|
69 |
public static final String LOCALECHANGE_PROPERTY = "localechange"; |
public static final String LOCALECHANGE_PROPERTY = "localechange"; |
70 |
public static final String NO_TRANSLATION = "NO TRANSLATION"; |
public static final String NO_TRANSLATION = "NO TRANSLATION"; |
71 |
public static final String DEFAULT_KEY = "default"; |
public static final String DEFAULT_KEY = "default"; |
72 |
static final Logger log = Logger.getLogger(Translation.class); |
static final Logger LOGGER = Logger.getLogger(Translation.class); |
73 |
static String activeLang = Locale.getDefault().getLanguage(); |
static String activeLang = Locale.getDefault().getLanguage(); |
74 |
|
|
75 |
static protected List<PropertyChangeListener> listeners = new ArrayList<PropertyChangeListener>(); |
static protected WeakHashSet<PropertyChangeListener> listeners = new WeakHashSet<PropertyChangeListener>( |
76 |
|
PropertyChangeListener.class); |
77 |
|
|
78 |
static { |
static { |
79 |
|
|
85 |
setActiveLang(locale.getLanguage()); |
setActiveLang(locale.getLanguage()); |
86 |
} |
} |
87 |
|
|
88 |
|
private WeakHashSet<ActionListener> actionListeners = new WeakHashSet<ActionListener>( |
89 |
|
ActionListener.class); |
90 |
|
|
91 |
@Override |
@Override |
92 |
/* |
/* |
93 |
* @comment To make a copy of a translation see methods toOneLine() and |
* @comment To make a copy of a translation see methods toOneLine() and |
94 |
* fromOneLine() |
* fromOneLine() |
95 |
*/ |
*/ |
96 |
public Translation clone() { |
public Translation clone() { |
97 |
return (Translation) super.clone(); |
throw new RuntimeException("use copy()"); |
98 |
|
// return (Translation) super.clone(); |
99 |
} |
} |
100 |
|
|
101 |
/** |
/** |
108 |
/** |
/** |
109 |
* Set up the {@link Translation}-system to use language. If a change is |
* Set up the {@link Translation}-system to use language. If a change is |
110 |
* performed, events are fired to listeners. Nothing is done if the new |
* performed, events are fired to listeners. Nothing is done if the new |
111 |
* language equals the old language. The system's default locale is changed. |
* language equals the old language. The system's default {@link Locale} is changed. |
112 |
* |
* |
113 |
* @param newLang |
* @param newLang |
114 |
* The ISO Code of the new active language |
* The ISO Code of the new active language |
152 |
|
|
153 |
fireLocaleChangeEvents(); |
fireLocaleChangeEvents(); |
154 |
|
|
155 |
log.info("skrueger.i8n.Translation switched ActiveLang to " + newLang); |
LOGGER.info("skrueger.i8n.Translation switched ActiveLang to " |
156 |
|
+ newLang); |
157 |
} |
} |
158 |
|
|
159 |
/** |
/** |
185 |
put(DEFAULT_KEY, defaultTranslation); |
put(DEFAULT_KEY, defaultTranslation); |
186 |
} else |
} else |
187 |
for (String code : languages) { |
for (String code : languages) { |
188 |
if (code.equals(getActiveLang())) { |
// if (code.equals(getActiveLang())) { |
189 |
put(code, defaultTranslation); |
put(code, defaultTranslation); |
190 |
} |
// } |
191 |
} |
} |
192 |
} |
} |
193 |
|
|
206 |
* <li>If format can't be recognized, the {@link String} is interpreted as |
* <li>If format can't be recognized, the {@link String} is interpreted as |
207 |
* the translation in the <code>{@value #DEFAULT_KEY}</code> language |
* the translation in the <code>{@value #DEFAULT_KEY}</code> language |
208 |
* |
* |
209 |
* @author Stefan Alfons Krüger |
* @author Stefan Alfons Tzeggai |
210 |
*/ |
*/ |
211 |
public void fromOneLine(final String oneLineCoded) { |
public void fromOneLine(final String oneLineCoded) { |
212 |
|
|
213 |
clear(); |
clear(); |
|
if ((oneLineCoded == null) || (oneLineCoded.equals(""))) { |
|
|
put(DEFAULT_KEY, ""); |
|
|
return; |
|
|
} |
|
214 |
|
|
215 |
if (oneLineCoded.indexOf("}") == -1) { |
try { |
216 |
// log.warn("The String '"+oneLineCoded+"' is not in oneLine coded => put(DEFAULT_KEY,oneLineCoded);"); |
|
217 |
put(DEFAULT_KEY, oneLineCoded); |
if ((oneLineCoded == null) || (oneLineCoded.equals(""))) { |
218 |
} |
put(DEFAULT_KEY, ""); |
219 |
|
return; |
220 |
String eatUp = oneLineCoded; |
} |
221 |
while (eatUp.indexOf("}") != -1) { |
|
222 |
String substring = eatUp.substring(0, eatUp.indexOf("}")); |
if (oneLineCoded.indexOf("}") == -1) { |
223 |
|
// log.warn("The String '"+oneLineCoded+"' is not in oneLine coded => put(DEFAULT_KEY,oneLineCoded);"); |
224 |
// log.debug("substring = "+substring); |
put(DEFAULT_KEY, oneLineCoded); |
225 |
String key = substring.substring(0, substring.indexOf("{")); |
} |
226 |
String value = substring.substring(substring.indexOf("{") + 1, |
|
227 |
substring.length()); |
String eatUp = oneLineCoded; |
228 |
// log.debug("key="+key); |
while (eatUp.indexOf("}") != -1) { |
229 |
// log.debug("value="+value); |
String substring = eatUp.substring(0, eatUp.indexOf("}")); |
230 |
put(key, value); |
|
231 |
eatUp = eatUp.substring(eatUp.indexOf("}") + 1); |
// log.debug("substring = "+substring); |
232 |
|
String key = substring.substring(0, substring.indexOf("{")); |
233 |
|
String value = substring.substring(substring.indexOf("{") + 1, |
234 |
|
substring.length()); |
235 |
|
// log.debug("key="+key); |
236 |
|
// log.debug("value="+value); |
237 |
|
put(key, value); |
238 |
|
eatUp = eatUp.substring(eatUp.indexOf("}") + 1); |
239 |
|
} |
240 |
|
} catch (Exception e) { |
241 |
|
LOGGER.warn("Error while reading the oneLineCode '" + oneLineCoded |
242 |
|
+ "'", e); |
243 |
|
LOGGER.warn("Translation will be empty!"); |
244 |
} |
} |
245 |
} |
} |
246 |
|
|
247 |
/** |
/** |
248 |
* Exports the Translations to a String of the Format: "de{Baum}en{tree}" |
* Exports the Translations to a String of the Format: "de{Baum}en{tree}" |
249 |
* |
* |
250 |
* @author Stefan Alfons Krüger |
* @author Stefan Alfons Tzeggai |
251 |
*/ |
*/ |
252 |
public String toOneLine() { |
public String toOneLine() { |
253 |
return I8NUtil.toOneLine(this); |
return I8NUtil.toOneLine(this); |
278 |
// MS: |
// MS: |
279 |
else { |
else { |
280 |
if (get(DEFAULT_KEY) != null) { |
if (get(DEFAULT_KEY) != null) { |
|
// log.debug("default lang returned, cuz the translation to "+activeLang+" was not found. Schmeiss raus martin, wenn du das mit der default trans geklärt hast."); |
|
281 |
return get(DEFAULT_KEY); |
return get(DEFAULT_KEY); |
282 |
} |
} |
283 |
|
|
288 |
return s; |
return s; |
289 |
} |
} |
290 |
} |
} |
291 |
// log.warn("No translation found!"); |
// log.warn("No translation found!"); |
292 |
return NO_TRANSLATION; |
return NO_TRANSLATION; |
293 |
} |
} |
294 |
|
|
295 |
/** |
/** |
296 |
* Copy this {@link Translation} to another {@link Translation} e.g. for |
* {@link PropertyChangeListener} can be registered to be informed when the |
297 |
* editing |
* {@link Locale} changed.<br> |
298 |
|
* The listeners are kept in a {@link WeakHashMap}, so you have to keep a |
299 |
|
* reference to the listener or it will be removed! |
300 |
* |
* |
301 |
* @return the destination {@link Translation} |
* @param propertyChangeListener |
302 |
|
* A {@link PropertyChangeListener} that will be called when |
303 |
|
* {@link #setActiveLang(String)} changes the language. |
304 |
*/ |
*/ |
305 |
public Translation copy(Translation backup) { |
public static void addLocaleChangeListener( |
306 |
if (backup == null) |
PropertyChangeListener propertyChangeListener) { |
307 |
throw new IllegalArgumentException( |
listeners.add(propertyChangeListener); |
|
"Target translation may not be null."); |
|
|
for (String s : keySet()) { |
|
|
backup.put(s, get(s)); |
|
|
} |
|
|
return backup; |
|
308 |
} |
} |
309 |
|
|
310 |
/** |
/** |
311 |
* {@link PropertyChangeListener} can be registered to be informed when the |
* {@link PropertyChangeListener} can be registered to be informed when the |
312 |
* {@link Locale} changed. |
* {@link Locale} changed.<br> |
313 |
|
* The listeners are kept in a {@link WeakHashMap}, so you have to keep a |
314 |
|
* reference to the listener or it will be removed! |
315 |
* |
* |
316 |
* @param propertyChangeListener |
* @param propertyChangeListener |
317 |
|
* A {@link PropertyChangeListener} that will be called when |
318 |
|
* {@link #setActiveLang(String)} changes the language. |
319 |
*/ |
*/ |
320 |
public static void addLocaleChangeListener( |
public static boolean removeLocaleChangeListener( |
321 |
PropertyChangeListener propertyChangeListener) { |
PropertyChangeListener propertyChangeListener) { |
322 |
listeners.add(propertyChangeListener); |
return listeners.remove(propertyChangeListener); |
323 |
} |
} |
324 |
|
|
325 |
/** |
/** |
336 |
} |
} |
337 |
} |
} |
338 |
|
|
339 |
|
/** |
340 |
|
* The listeneras are stored in a {@link WeakHashSet}! So you HAVE TO KEEP a |
341 |
|
* reference as long as you need the listener. |
342 |
|
*/ |
343 |
|
public void addTranslationChangeListener(ActionListener actionListener) { |
344 |
|
if (actionListeners.add(actionListener)) { |
345 |
|
// LOGGER |
346 |
|
// .debug("registering a new translationChangeActionListener in the WeakHashSet"); |
347 |
|
} |
348 |
|
} |
349 |
|
|
350 |
|
/** |
351 |
|
* The listeneras are stored in a {@link WeakHashSet}! You don't have to |
352 |
|
* remove the listener, as long as you throw away the reference to the |
353 |
|
* listener. |
354 |
|
*/ |
355 |
|
public boolean removeTranslationChangeListener(ActionListener actionListener) { |
356 |
|
return actionListeners.remove(actionListener); |
357 |
|
} |
358 |
|
|
359 |
|
public void fireTranslationChangedEvents(String lang) { |
360 |
|
ActionEvent ae = new ActionEvent(this, new Random().nextInt(), lang); |
361 |
|
|
362 |
|
final Iterator<ActionListener> iterator = actionListeners.iterator(); |
363 |
|
while (iterator.hasNext()) { |
364 |
|
ActionListener al = iterator.next(); |
365 |
|
al.actionPerformed(ae); |
366 |
|
} |
367 |
|
} |
368 |
|
|
369 |
|
@Override |
370 |
|
public String put(String lang, String value) { |
371 |
|
String result = super.put(lang, value); |
372 |
|
fireTranslationChangedEvents(lang); |
373 |
|
return result; |
374 |
|
} |
375 |
|
|
376 |
|
public void fromOneLine(InternationalString iString) { |
377 |
|
if (iString != null) |
378 |
|
fromOneLine(iString.toString()); |
379 |
|
else |
380 |
|
fromOneLine((String) null); |
381 |
|
} |
382 |
|
|
383 |
|
/** |
384 |
|
* Copy this {@link Translation} to another {@link Translation} e.g. for |
385 |
|
* editing |
386 |
|
* |
387 |
|
* @return the destination {@link Translation} |
388 |
|
*/ |
389 |
|
@Override |
390 |
|
public Translation copyTo(Translation translation2) { |
391 |
|
|
392 |
|
if (translation2 == null) |
393 |
|
// throw new IllegalArgumentException( |
394 |
|
// "Target translation may not be null."); |
395 |
|
return copy(); |
396 |
|
for (String s : keySet()) { |
397 |
|
translation2.put(s, get(s)); |
398 |
|
} |
399 |
|
|
400 |
|
return translation2; |
401 |
|
} |
402 |
|
|
403 |
|
@Override |
404 |
|
public Translation copy() { |
405 |
|
return copyTo(new Translation()); |
406 |
|
} |
407 |
|
|
408 |
|
/** |
409 |
|
* Checks if the {@link String}s stored in the {@link Translation} are all |
410 |
|
* valid. |
411 |
|
* |
412 |
|
* @return <code>true</code> if all good |
413 |
|
*/ |
414 |
|
public static boolean checkValid(Translation translationToCheck) { |
415 |
|
|
416 |
|
for (String l : translationToCheck.values()) { |
417 |
|
|
418 |
|
if (l.contains("{") || l.contains("}")) { |
419 |
|
|
420 |
|
return false; |
421 |
|
} |
422 |
|
} |
423 |
|
return true; |
424 |
|
} |
425 |
|
|
426 |
|
/** |
427 |
|
* Goes through the available languages of the FIRST registered {@link ResourceProvider} and set the active locale to the fist match. |
428 |
|
* |
429 |
|
* @param fireChangeEvent if <code>true</code>, a Translation.fireLocaleChangeEvents() is issued. |
430 |
|
|
431 |
|
* @return |
432 |
|
*/ |
433 |
|
public static boolean setFirstmatchingLanguage(List<String> languages, |
434 |
|
boolean fireChangeEvent) { |
435 |
|
|
436 |
|
SortableVector<ResourceProvider> registeredResourceProvider = ResourceProvider |
437 |
|
.getRegisteredResourceProvider(); |
438 |
|
Set<Locale> available = ResourceProvider.getAvailableLocales( |
439 |
|
registeredResourceProvider.get(0), true); |
440 |
|
|
441 |
|
for (String l : languages) { |
442 |
|
for (Locale loc : available) { |
443 |
|
if (loc.getLanguage().equals(l)) { |
444 |
|
Translation.setActiveLang(l); |
445 |
|
if (fireChangeEvent) |
446 |
|
Translation.fireLocaleChangeEvents(); |
447 |
|
return true; |
448 |
|
} |
449 |
|
} |
450 |
|
} |
451 |
|
|
452 |
|
return false; |
453 |
|
|
454 |
|
} |
455 |
} |
} |