3 * Copyright © 2008 David Roden
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.
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.
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.
20 package net.pterodactylus.util.i18n;
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;
37 import javax.swing.KeyStroke;
39 import net.pterodactylus.util.logging.Logging;
40 import de.ina.util.io.Closer;
43 * Class that handles i18n.
45 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
50 private static final Logger logger = Logging.getLogger(I18n.class.getName());
52 /** The base name of the resource files. */
53 private String resourceName;
55 /** The class whose class loader is used to load resource files. */
56 private final Class<?> resourceClass;
58 /** List of I18nables that are notified when the language changes. */
59 private final List<I18nable> i18nables = new ArrayList<I18nable>();
61 /** The current locale. */
62 private Locale currentLocale;
64 /** The default language. */
65 private Properties defaultLanguage;
67 /** The current language. */
68 private Properties currentLanguage;
71 * Creates a new I18n container.
74 * The base name of the language resource files
75 * @param resourceClass
76 * The class whose class loader is used to load resource files
78 public I18n(String resourceName, Class<?> resourceClass) {
79 this.resourceName = resourceName;
80 this.resourceClass = resourceClass;
81 defaultLanguage = new Properties();
82 InputStream inputStream = null;
84 inputStream = resourceClass.getResourceAsStream(resourceName + ".properties");
85 if (inputStream != null) {
86 defaultLanguage.load(inputStream);
88 } catch (IOException e) {
89 /* something is fucked. */
91 setLocale(Locale.getDefault(), false);
95 * Returns the translated value for a key. The translated values may contain
96 * placeholders that are replaced with the given parameters.
102 * The parameters in case the translated value contains
104 * @return The translated message, or the key itself if no translation could
107 public String get(String key, Object... parameters) {
109 value = currentLanguage.getProperty(key);
111 logger.log(Level.WARNING, "please fix “" + key + "”!", new Throwable());
112 /* TODO - replace with value when done! */
115 if ((parameters != null) && (parameters.length > 0)) {
116 return MessageFormat.format(value, parameters);
122 * Returns the keycode from the value of the given key. You can specify the
123 * constants in {@link KeyEvent} in the properties file, e.g. VK_S for the
124 * keycode ‘s’ when used for mnemonics.
127 * The key under which the keycode is stored
128 * @return The keycode
130 public int getKey(String key) {
131 String value = currentLanguage.getProperty(key);
132 if ((value != null) && value.startsWith("VK_")) {
134 Field field = KeyEvent.class.getField(value);
135 return field.getInt(null);
136 } catch (SecurityException e) {
138 } catch (NoSuchFieldException e) {
140 } catch (IllegalArgumentException e) {
142 } catch (IllegalAccessException e) {
146 System.err.println("please fix “" + key + "”!");
147 return KeyEvent.VK_UNDEFINED;
151 * Returns a key stroke for use with swing accelerators.
154 * The key of the key stroke
155 * @return The key stroke, or <code>null</code> if no key stroke could be
156 * created from the translated value
158 public KeyStroke getKeyStroke(String key) {
159 String value = currentLanguage.getProperty(key);
163 StringTokenizer keyTokens = new StringTokenizer(value, "+- ");
164 int modifierMask = 0;
165 while (keyTokens.hasMoreTokens()) {
166 String keyToken = keyTokens.nextToken();
167 if ("ctrl".equalsIgnoreCase(keyToken)) {
168 modifierMask |= InputEvent.CTRL_DOWN_MASK;
169 } else if ("alt".equalsIgnoreCase(keyToken)) {
170 modifierMask |= InputEvent.ALT_DOWN_MASK;
171 } else if ("shift".equalsIgnoreCase(keyToken)) {
172 modifierMask |= InputEvent.SHIFT_DOWN_MASK;
174 if (keyToken.startsWith("VK_")) {
175 if (keyToken.equals("VK_UNDEFINED")) {
179 Field field = KeyEvent.class.getField(keyToken);
180 return KeyStroke.getKeyStroke(field.getInt(null), modifierMask);
181 } catch (SecurityException e) {
183 } catch (NoSuchFieldException e) {
185 } catch (IllegalArgumentException e) {
187 } catch (IllegalAccessException e) {
191 return KeyStroke.getKeyStroke(keyToken.charAt(0), modifierMask);
198 * Sets the current locale.
201 * The new locale to use
203 public void setLocale(Locale newLocale) {
204 setLocale(newLocale, true);
208 * Sets the current locale.
211 * The new locale to use
213 * <code>true</code> to notify registered {@link I18nable}s after
214 * the language was changed
216 private void setLocale(Locale newLocale, boolean notify) {
217 currentLocale = newLocale;
218 InputStream inputStream = null;
220 currentLanguage = new Properties(defaultLanguage);
221 if (newLocale == Locale.ENGLISH) {
227 inputStream = resourceClass.getResourceAsStream(resourceName + "_" + newLocale.getLanguage() + ".properties");
228 if (inputStream != null) {
229 currentLanguage.load(inputStream);
234 } catch (MissingResourceException mre1) {
235 currentLocale = Locale.ENGLISH;
236 } catch (IOException ioe1) {
237 currentLocale = Locale.ENGLISH;
239 Closer.close(inputStream);
244 * Returns the current locale.
246 * @return The current locale
248 public Locale getLocale() {
249 return currentLocale;
253 * Finds all available locales.
255 * @return All available locales
257 public static List<Locale> findAvailableLanguages() {
258 List<Locale> availableLanguages = new ArrayList<Locale>();
259 availableLanguages.add(Locale.ENGLISH);
260 availableLanguages.add(Locale.GERMAN);
261 return availableLanguages;
265 * Registers the given I18nable to be updated when the language is changed.
268 * The i18nable to register
270 public void registerI18nable(I18nable i18nable) {
271 i18nables.add(i18nable);
275 * Deregisters the given I18nable to be updated when the language is
279 * The i18nable to register
281 public void deregisterI18nable(I18nable i18nable) {
282 i18nables.remove(i18nable);
290 * Notifies all registered {@link I18nable}s that the language was changed.
292 private void notifyI18nables() {
293 for (I18nable i18nable : i18nables) {
294 i18nable.updateI18n(this);