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