8223a0b772fbdc2abf7193c1f209221267c56353
[jSite2.git] / I18n.java
1 /*
2  * jSite2 - I18n.java -
3  * Copyright © 2008 David Roden
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (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
20 package net.pterodactylus.jsite.i18n;
21
22 import java.awt.event.InputEvent;
23 import java.awt.event.KeyEvent;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.lang.reflect.Field;
27 import java.text.MessageFormat;
28 import java.util.ArrayList;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.MissingResourceException;
32 import java.util.Properties;
33 import java.util.StringTokenizer;
34 import java.util.logging.Level;
35 import java.util.logging.Logger;
36
37 import javax.swing.KeyStroke;
38
39 import net.pterodactylus.util.io.Closer;
40 import net.pterodactylus.util.logging.Logging;
41
42 /**
43  * Class that handles i18n.
44  * 
45  * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
46  */
47 public class I18n {
48
49         /** Logger. */
50         private static final Logger logger = Logging.getLogger(I18n.class.getName());
51
52         /** List of I18nables that are notified when the language changes. */
53         private static final List<I18nable> i18nables = new ArrayList<I18nable>();
54
55         /** The current locale. */
56         private static Locale currentLocale;
57
58         /** The default language. */
59         private static Properties defaultLanguage;
60
61         /** The current language. */
62         private static Properties currentLanguage;
63
64         static {
65                 defaultLanguage = new Properties();
66                 InputStream inputStream = null;
67                 try {
68                         inputStream = I18n.class.getResourceAsStream("jSite.properties");
69                         if (inputStream != null) {
70                                 defaultLanguage.load(inputStream);
71                         }
72                 } catch (IOException e) {
73                         /* something is fucked. */
74                 }
75                 setLocale(Locale.getDefault(), false);
76         }
77
78         /**
79          * Returns the translated value for a key. The translated values may contain
80          * placeholders that are replaced with the given parameters.
81          * 
82          * @see MessageFormat
83          * @param key
84          *            The key to get
85          * @param parameters
86          *            The parameters in case the translated value contains
87          *            placeholders
88          * @return The translated message, or the key itself if no translation could
89          *         be found
90          */
91         public static String get(String key, Object... parameters) {
92                 String value = null;
93                 value = currentLanguage.getProperty(key);
94                 if (value == null) {
95                         logger.log(Level.WARNING, "please fix “" + key + "”!", new Throwable());
96                         /* TODO - replace with value when done! */
97                         return null;
98                 }
99                 if ((parameters != null) && (parameters.length > 0)) {
100                         return MessageFormat.format(value, parameters);
101                 }
102                 return value;
103         }
104
105         /**
106          * Returns the keycode from the value of the given key. You can specify the
107          * constants in {@link KeyEvent} in the properties file, e.g. VK_S for the
108          * keycode ‘s’ when used for mnemonics.
109          * 
110          * @param key
111          *            The key under which the keycode is stored
112          * @return The keycode
113          */
114         public static int getKey(String key) {
115                 String value = currentLanguage.getProperty(key);
116                 if ((value != null) && value.startsWith("VK_")) {
117                         try {
118                                 Field field = KeyEvent.class.getField(value);
119                                 return field.getInt(null);
120                         } catch (SecurityException e) {
121                                 /* ignore. */
122                         } catch (NoSuchFieldException e) {
123                                 /* ignore. */
124                         } catch (IllegalArgumentException e) {
125                                 /* ignore. */
126                         } catch (IllegalAccessException e) {
127                                 /* ignore. */
128                         }
129                 }
130                 System.err.println("please fix “" + key + "”!");
131                 return KeyEvent.VK_UNDEFINED;
132         }
133
134         /**
135          * Returns a key stroke for use with swing accelerators.
136          * 
137          * @param key
138          *            The key of the key stroke
139          * @return The key stroke, or <code>null</code> if no key stroke could be
140          *         created from the translated value
141          */
142         public static KeyStroke getKeyStroke(String key) {
143                 String value = currentLanguage.getProperty(key);
144                 if (value == null) {
145                         return null;
146                 }
147                 StringTokenizer keyTokens = new StringTokenizer(value, "+- ");
148                 int modifierMask = 0;
149                 while (keyTokens.hasMoreTokens()) {
150                         String keyToken = keyTokens.nextToken();
151                         if ("ctrl".equalsIgnoreCase(keyToken)) {
152                                 modifierMask |= InputEvent.CTRL_DOWN_MASK;
153                         } else if ("alt".equalsIgnoreCase(keyToken)) {
154                                 modifierMask |= InputEvent.ALT_DOWN_MASK;
155                         } else if ("shift".equalsIgnoreCase(keyToken)) {
156                                 modifierMask |= InputEvent.SHIFT_DOWN_MASK;
157                         } else {
158                                 if (keyToken.startsWith("VK_")) {
159                                         if (keyToken.equals("VK_UNDEFINED")) {
160                                                 return null;
161                                         }
162                                         try {
163                                                 Field field = KeyEvent.class.getField(keyToken);
164                                                 return KeyStroke.getKeyStroke(field.getInt(null), modifierMask);
165                                         } catch (SecurityException e) {
166                                                 /* ignore. */
167                                         } catch (NoSuchFieldException e) {
168                                                 /* ignore. */
169                                         } catch (IllegalArgumentException e) {
170                                                 /* ignore. */
171                                         } catch (IllegalAccessException e) {
172                                                 /* ignore. */
173                                         }
174                                 }
175                                 return KeyStroke.getKeyStroke(keyToken.charAt(0), modifierMask);
176                         }
177                 }
178                 return null;
179         }
180
181         /**
182          * Sets the current locale.
183          * 
184          * @param newLocale
185          *            The new locale to use
186          */
187         public static void setLocale(Locale newLocale) {
188                 setLocale(newLocale, true);
189         }
190
191         /**
192          * Sets the current locale.
193          * 
194          * @param newLocale
195          *            The new locale to use
196          * @param notify
197          *            <code>true</code> to notify registered {@link I18nable}s
198          *            after the language was changed
199          */
200         private static void setLocale(Locale newLocale, boolean notify) {
201                 currentLocale = newLocale;
202                 InputStream inputStream = null;
203                 try {
204                         currentLanguage = new Properties(defaultLanguage);
205                         if (newLocale == Locale.ENGLISH) {
206                                 if (notify) {
207                                         notifyI18nables();
208                                 }
209                                 return;
210                         }
211                         inputStream = I18n.class.getResourceAsStream("jSite_" + newLocale.getLanguage() + ".properties");
212                         if (inputStream != null) {
213                                 currentLanguage.load(inputStream);
214                                 if (notify) {
215                                         notifyI18nables();
216                                 }
217                         }
218                 } catch (MissingResourceException mre1) {
219                         currentLocale = Locale.ENGLISH;
220                 } catch (IOException ioe1) {
221                         currentLocale = Locale.ENGLISH;
222                 } finally {
223                         Closer.close(inputStream);
224                 }
225         }
226
227         /**
228          * Returns the current locale.
229          * 
230          * @return The current locale
231          */
232         public static Locale getLocale() {
233                 return currentLocale;
234         }
235
236         /**
237          * Finds all available locales.
238          * 
239          * @return All available locales
240          */
241         public static List<Locale> findAvailableLanguages() {
242                 List<Locale> availableLanguages = new ArrayList<Locale>();
243                 availableLanguages.add(Locale.ENGLISH);
244                 availableLanguages.add(Locale.GERMAN);
245                 return availableLanguages;
246         }
247
248         /**
249          * Registers the given I18nable to be updated when the language is changed.
250          * 
251          * @param i18nable
252          *            The i18nable to register
253          */
254         public static void registerI18nable(I18nable i18nable) {
255                 i18nables.add(i18nable);
256         }
257
258         /**
259          * Deregisters the given I18nable to be updated when the language is
260          * changed.
261          * 
262          * @param i18nable
263          *            The i18nable to register
264          */
265         public static void deregisterI18nable(I18nable i18nable) {
266                 i18nables.remove(i18nable);
267         }
268
269         //
270         // PRIVATE METHODS
271         //
272
273         /**
274          * Notifies all registered {@link I18nable}s that the language was changed.
275          */
276         private static void notifyI18nables() {
277                 for (I18nable i18nable: i18nables) {
278                         i18nable.updateI18n();
279                 }
280         }
281
282 }