Add i18n components.
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Tue, 27 Jan 2009 08:16:18 +0000 (09:16 +0100)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Tue, 27 Jan 2009 08:16:18 +0000 (09:16 +0100)
src/net/pterodactylus/util/i18n/I18n.java [new file with mode: 0644]
src/net/pterodactylus/util/i18n/I18nable.java [new file with mode: 0644]
src/net/pterodactylus/util/i18n/gui/I18nAction.java [new file with mode: 0644]
src/net/pterodactylus/util/i18n/gui/I18nLabel.java [new file with mode: 0644]
src/net/pterodactylus/util/i18n/gui/I18nMenu.java [new file with mode: 0644]
src/net/pterodactylus/util/i18n/gui/package-info.java [new file with mode: 0644]

diff --git a/src/net/pterodactylus/util/i18n/I18n.java b/src/net/pterodactylus/util/i18n/I18n.java
new file mode 100644 (file)
index 0000000..e6f30b9
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * jSite2 - I18n.java -
+ * Copyright © 2008 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package net.pterodactylus.util.i18n;
+
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.swing.KeyStroke;
+
+import net.pterodactylus.util.logging.Logging;
+import de.ina.util.io.Closer;
+
+/**
+ * Class that handles i18n.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class I18n {
+
+       /** Logger. */
+       private static final Logger logger = Logging.getLogger(I18n.class.getName());
+
+       /** List of I18nables that are notified when the language changes. */
+       private static final List<I18nable> i18nables = new ArrayList<I18nable>();
+
+       /** The current locale. */
+       private static Locale currentLocale;
+
+       /** The default language. */
+       private static Properties defaultLanguage;
+
+       /** The current language. */
+       private static Properties currentLanguage;
+
+       static {
+               defaultLanguage = new Properties();
+               InputStream inputStream = null;
+               try {
+                       inputStream = I18n.class.getResourceAsStream("jkeytool.properties");
+                       if (inputStream != null) {
+                               defaultLanguage.load(inputStream);
+                       }
+               } catch (IOException e) {
+                       /* something is fucked. */
+               }
+               setLocale(Locale.getDefault(), false);
+       }
+
+       /**
+        * Returns the translated value for a key. The translated values may contain
+        * placeholders that are replaced with the given parameters.
+        *
+        * @see MessageFormat
+        * @param key
+        *            The key to get
+        * @param parameters
+        *            The parameters in case the translated value contains
+        *            placeholders
+        * @return The translated message, or the key itself if no translation could
+        *         be found
+        */
+       public static String get(String key, Object... parameters) {
+               String value = null;
+               value = currentLanguage.getProperty(key);
+               if (value == null) {
+                       logger.log(Level.WARNING, "please fix “" + key + "”!", new Throwable());
+                       /* TODO - replace with value when done! */
+                       return null;
+               }
+               if ((parameters != null) && (parameters.length > 0)) {
+                       return MessageFormat.format(value, parameters);
+               }
+               return value;
+       }
+
+       /**
+        * Returns the keycode from the value of the given key. You can specify the
+        * constants in {@link KeyEvent} in the properties file, e.g. VK_S for the
+        * keycode ‘s’ when used for mnemonics.
+        *
+        * @param key
+        *            The key under which the keycode is stored
+        * @return The keycode
+        */
+       public static int getKey(String key) {
+               String value = currentLanguage.getProperty(key);
+               if ((value != null) && value.startsWith("VK_")) {
+                       try {
+                               Field field = KeyEvent.class.getField(value);
+                               return field.getInt(null);
+                       } catch (SecurityException e) {
+                               /* ignore. */
+                       } catch (NoSuchFieldException e) {
+                               /* ignore. */
+                       } catch (IllegalArgumentException e) {
+                               /* ignore. */
+                       } catch (IllegalAccessException e) {
+                               /* ignore. */
+                       }
+               }
+               System.err.println("please fix “" + key + "”!");
+               return KeyEvent.VK_UNDEFINED;
+       }
+
+       /**
+        * Returns a key stroke for use with swing accelerators.
+        *
+        * @param key
+        *            The key of the key stroke
+        * @return The key stroke, or <code>null</code> if no key stroke could be
+        *         created from the translated value
+        */
+       public static KeyStroke getKeyStroke(String key) {
+               String value = currentLanguage.getProperty(key);
+               if (value == null) {
+                       return null;
+               }
+               StringTokenizer keyTokens = new StringTokenizer(value, "+- ");
+               int modifierMask = 0;
+               while (keyTokens.hasMoreTokens()) {
+                       String keyToken = keyTokens.nextToken();
+                       if ("ctrl".equalsIgnoreCase(keyToken)) {
+                               modifierMask |= InputEvent.CTRL_DOWN_MASK;
+                       } else if ("alt".equalsIgnoreCase(keyToken)) {
+                               modifierMask |= InputEvent.ALT_DOWN_MASK;
+                       } else if ("shift".equalsIgnoreCase(keyToken)) {
+                               modifierMask |= InputEvent.SHIFT_DOWN_MASK;
+                       } else {
+                               if (keyToken.startsWith("VK_")) {
+                                       if (keyToken.equals("VK_UNDEFINED")) {
+                                               return null;
+                                       }
+                                       try {
+                                               Field field = KeyEvent.class.getField(keyToken);
+                                               return KeyStroke.getKeyStroke(field.getInt(null), modifierMask);
+                                       } catch (SecurityException e) {
+                                               /* ignore. */
+                                       } catch (NoSuchFieldException e) {
+                                               /* ignore. */
+                                       } catch (IllegalArgumentException e) {
+                                               /* ignore. */
+                                       } catch (IllegalAccessException e) {
+                                               /* ignore. */
+                                       }
+                               }
+                               return KeyStroke.getKeyStroke(keyToken.charAt(0), modifierMask);
+                       }
+               }
+               return null;
+       }
+
+       /**
+        * Sets the current locale.
+        *
+        * @param newLocale
+        *            The new locale to use
+        */
+       public static void setLocale(Locale newLocale) {
+               setLocale(newLocale, true);
+       }
+
+       /**
+        * Sets the current locale.
+        *
+        * @param newLocale
+        *            The new locale to use
+        * @param notify
+        *            <code>true</code> to notify registered {@link I18nable}s
+        *            after the language was changed
+        */
+       private static void setLocale(Locale newLocale, boolean notify) {
+               currentLocale = newLocale;
+               InputStream inputStream = null;
+               try {
+                       currentLanguage = new Properties(defaultLanguage);
+                       if (newLocale == Locale.ENGLISH) {
+                               if (notify) {
+                                       notifyI18nables();
+                               }
+                               return;
+                       }
+                       inputStream = I18n.class.getResourceAsStream("jSite_" + newLocale.getLanguage() + ".properties");
+                       if (inputStream != null) {
+                               currentLanguage.load(inputStream);
+                               if (notify) {
+                                       notifyI18nables();
+                               }
+                       }
+               } catch (MissingResourceException mre1) {
+                       currentLocale = Locale.ENGLISH;
+               } catch (IOException ioe1) {
+                       currentLocale = Locale.ENGLISH;
+               } finally {
+                       Closer.close(inputStream);
+               }
+       }
+
+       /**
+        * Returns the current locale.
+        *
+        * @return The current locale
+        */
+       public static Locale getLocale() {
+               return currentLocale;
+       }
+
+       /**
+        * Finds all available locales.
+        *
+        * @return All available locales
+        */
+       public static List<Locale> findAvailableLanguages() {
+               List<Locale> availableLanguages = new ArrayList<Locale>();
+               availableLanguages.add(Locale.ENGLISH);
+               availableLanguages.add(Locale.GERMAN);
+               return availableLanguages;
+       }
+
+       /**
+        * Registers the given I18nable to be updated when the language is changed.
+        *
+        * @param i18nable
+        *            The i18nable to register
+        */
+       public static void registerI18nable(I18nable i18nable) {
+               i18nables.add(i18nable);
+       }
+
+       /**
+        * Deregisters the given I18nable to be updated when the language is
+        * changed.
+        *
+        * @param i18nable
+        *            The i18nable to register
+        */
+       public static void deregisterI18nable(I18nable i18nable) {
+               i18nables.remove(i18nable);
+       }
+
+       //
+       // PRIVATE METHODS
+       //
+
+       /**
+        * Notifies all registered {@link I18nable}s that the language was changed.
+        */
+       private static void notifyI18nables() {
+               for (I18nable i18nable: i18nables) {
+                       i18nable.updateI18n();
+               }
+       }
+
+}
diff --git a/src/net/pterodactylus/util/i18n/I18nable.java b/src/net/pterodactylus/util/i18n/I18nable.java
new file mode 100644 (file)
index 0000000..c32fda6
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * jSite2 - I18nable.java -
+ * Copyright © 2008 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package net.pterodactylus.util.i18n;
+
+/**
+ * Interface for objects that want to be notified when the language is changed.
+ * 
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public interface I18nable {
+
+       /**
+        * Notifies the object that the language in {@link I18n} was changed.
+        */
+       public void updateI18n();
+
+}
diff --git a/src/net/pterodactylus/util/i18n/gui/I18nAction.java b/src/net/pterodactylus/util/i18n/gui/I18nAction.java
new file mode 100644 (file)
index 0000000..6078335
--- /dev/null
@@ -0,0 +1,89 @@
+
+package net.pterodactylus.util.i18n.gui;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.Icon;
+
+import net.pterodactylus.util.i18n.I18n;
+import net.pterodactylus.util.i18n.I18nable;
+
+/**
+ * Helper class that initializes actions with values from {@link I18n}.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public abstract class I18nAction extends AbstractAction implements I18nable {
+
+       /** The I18n basename. */
+       private final String i18nName;
+
+       /**
+        * Creates a new action that uses the given name as base name to get values
+        * from {@link I18n}.
+        *
+        * @param i18nName
+        *            The base name of the action
+        */
+       public I18nAction(String i18nName) {
+               this(i18nName, null);
+       }
+
+       /**
+        * Creates a new action that uses the given name as base name to get values
+        * from {@link I18n} and the given icon.
+        *
+        * @param i18nName
+        *            The base name of the action
+        * @param icon
+        *            The icon for the action
+        */
+       public I18nAction(String i18nName, Icon icon) {
+               this(i18nName, true, icon);
+       }
+
+       /**
+        * Creates a new action that uses the given name as base name to get values
+        * from {@link I18n}.
+        *
+        * @param i18nName
+        *            The base name of the action
+        * @param enabled
+        *            Whether the action should be enabled
+        */
+       public I18nAction(String i18nName, boolean enabled) {
+               this(i18nName, enabled, null);
+       }
+
+       /**
+        * Creates a new action that uses the given name as base name to get values
+        * from {@link I18n} and the given icon.
+        *
+        * @param i18nName
+        *            The base name of the action
+        * @param enabled
+        *            Whether the action should be enabled
+        * @param icon
+        *            The icon for the action
+        */
+       public I18nAction(String i18nName, boolean enabled, Icon icon) {
+               this.i18nName = i18nName;
+               if (icon != null) {
+                       putValue(Action.SMALL_ICON, icon);
+               }
+               setEnabled(enabled);
+               updateI18n();
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void updateI18n() {
+               putValue(Action.NAME, I18n.get(i18nName + ".name"));
+               putValue(Action.MNEMONIC_KEY, I18n.getKey(i18nName + ".mnemonic"));
+               putValue(Action.ACCELERATOR_KEY, I18n.getKeyStroke(i18nName + ".accelerator"));
+               putValue(Action.SHORT_DESCRIPTION, I18n.get(i18nName + ".shortDescription"));
+               putValue(Action.LONG_DESCRIPTION, I18n.get(i18nName + ".longDescription"));
+       }
+
+}
\ No newline at end of file
diff --git a/src/net/pterodactylus/util/i18n/gui/I18nLabel.java b/src/net/pterodactylus/util/i18n/gui/I18nLabel.java
new file mode 100644 (file)
index 0000000..32ad57d
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * jSite2 - I18nLabel.java -
+ * Copyright © 2008 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package net.pterodactylus.util.i18n.gui;
+
+import java.awt.Component;
+
+import javax.swing.JLabel;
+
+import net.pterodactylus.util.i18n.I18n;
+import net.pterodactylus.util.i18n.I18nable;
+
+/**
+ * Label that can update itself from {@link I18n}.
+ * 
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class I18nLabel extends JLabel implements I18nable {
+
+       /** The I18n basename of the label. */
+       private final String i18nBasename;
+
+       /** Optional arguments for i18n replacement. */
+       private final Object[] arguments;
+
+       /**
+        * Creates a new label with the given I18n basename.
+        * 
+        * @param i18nBasename
+        *            The I18n basename of the label
+        */
+       public I18nLabel(String i18nBasename) {
+               this(i18nBasename, (Component) null);
+       }
+
+       /**
+        * Creates a new label with the given I18n basename that optionally is a
+        * label for the given component.
+        * 
+        * @param i18nBasename
+        *            The I18n basename of the label
+        * @param component
+        *            The component that is activated by the label, or
+        *            <code>null</code> if this label should not activate a
+        *            component
+        */
+       public I18nLabel(String i18nBasename, Component component) {
+               this(i18nBasename, component, (Object[]) null);
+       }
+
+       /**
+        * Creates a new label with the given I18n basename that optionally is a
+        * label for the given component.
+        * 
+        * @param i18nBasename
+        *            The I18n basename of the label
+        * @param arguments
+        *            Optional arguments that are handed in to
+        *            {@link I18n#get(String, Object...)}
+        */
+       public I18nLabel(String i18nBasename, Object... arguments) {
+               this(i18nBasename, null, arguments);
+       }
+
+       /**
+        * Creates a new label with the given I18n basename that optionally is a
+        * label for the given component.
+        * 
+        * @param i18nBasename
+        *            The I18n basename of the label
+        * @param component
+        *            The component that is activated by the label, or
+        *            <code>null</code> if this label should not activate a
+        *            component
+        * @param arguments
+        *            Optional arguments that are handed in to
+        *            {@link I18n#get(String, Object...)}
+        */
+       public I18nLabel(String i18nBasename, Component component, Object... arguments) {
+               super();
+               this.i18nBasename = i18nBasename;
+               this.arguments = arguments;
+               if (component != null) {
+                       setLabelFor(component);
+               }
+               updateI18n();
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void updateI18n() {
+               setText(I18n.get(i18nBasename + ".name", arguments));
+               if (getLabelFor() != null) {
+                       setDisplayedMnemonic(I18n.getKey(i18nBasename + ".mnemonic"));
+               }
+       }
+
+}
diff --git a/src/net/pterodactylus/util/i18n/gui/I18nMenu.java b/src/net/pterodactylus/util/i18n/gui/I18nMenu.java
new file mode 100644 (file)
index 0000000..993be97
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * jSite2 - I18nMenu.java -
+ * Copyright © 2008 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package net.pterodactylus.util.i18n.gui;
+
+import javax.swing.JMenu;
+
+import net.pterodactylus.util.i18n.I18n;
+import net.pterodactylus.util.i18n.I18nable;
+
+/**
+ * Menu that receives its properties from {@link I18n}.
+ * 
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class I18nMenu extends JMenu implements I18nable {
+
+       /** The {@link I18n} basename. */
+       private final String i18nBasename;
+
+       /**
+        * Creates a new menu with the given {@link I18n} basename.
+        * 
+        * @param i18nBasename
+        *            The basename of the {@link I18n} properties
+        */
+       public I18nMenu(String i18nBasename) {
+               this.i18nBasename = i18nBasename;
+               updateI18n();
+       }
+
+       //
+       // INTERFACE I18nable
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void updateI18n() {
+               setText(I18n.get(i18nBasename + ".name"));
+               setMnemonic(I18n.getKey(i18nBasename + ".mnemonic"));
+       }
+
+}
diff --git a/src/net/pterodactylus/util/i18n/gui/package-info.java b/src/net/pterodactylus/util/i18n/gui/package-info.java
new file mode 100644 (file)
index 0000000..753d581
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * jSite2 - I18nMenu.java -
+ * Copyright © 2008 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * Contains various i18n-related Swing helper classes.
+ * 
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+
+package net.pterodactylus.util.i18n.gui;
\ No newline at end of file