From a3df2217d22c7a2d90320690126fd80dfb94a921 Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 27 Jan 2009 09:16:18 +0100 Subject: [PATCH] Add i18n components. --- src/net/pterodactylus/util/i18n/I18n.java | 282 +++++++++++++++++++++ src/net/pterodactylus/util/i18n/I18nable.java | 34 +++ .../pterodactylus/util/i18n/gui/I18nAction.java | 89 +++++++ src/net/pterodactylus/util/i18n/gui/I18nLabel.java | 115 +++++++++ src/net/pterodactylus/util/i18n/gui/I18nMenu.java | 60 +++++ .../pterodactylus/util/i18n/gui/package-info.java | 26 ++ 6 files changed, 606 insertions(+) create mode 100644 src/net/pterodactylus/util/i18n/I18n.java create mode 100644 src/net/pterodactylus/util/i18n/I18nable.java create mode 100644 src/net/pterodactylus/util/i18n/gui/I18nAction.java create mode 100644 src/net/pterodactylus/util/i18n/gui/I18nLabel.java create mode 100644 src/net/pterodactylus/util/i18n/gui/I18nMenu.java create mode 100644 src/net/pterodactylus/util/i18n/gui/package-info.java diff --git a/src/net/pterodactylus/util/i18n/I18n.java b/src/net/pterodactylus/util/i18n/I18n.java new file mode 100644 index 0000000..e6f30b9 --- /dev/null +++ b/src/net/pterodactylus/util/i18n/I18n.java @@ -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 <bombe@freenetproject.org> + */ +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 i18nables = new ArrayList(); + + /** 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 null 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 + * true 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 findAvailableLanguages() { + List availableLanguages = new ArrayList(); + 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 index 0000000..c32fda6 --- /dev/null +++ b/src/net/pterodactylus/util/i18n/I18nable.java @@ -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 <bombe@freenetproject.org> + */ +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 index 0000000..6078335 --- /dev/null +++ b/src/net/pterodactylus/util/i18n/gui/I18nAction.java @@ -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 <bombe@freenetproject.org> + */ +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 index 0000000..32ad57d --- /dev/null +++ b/src/net/pterodactylus/util/i18n/gui/I18nLabel.java @@ -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 <bombe@freenetproject.org> + */ +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 + * null 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 + * null 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 index 0000000..993be97 --- /dev/null +++ b/src/net/pterodactylus/util/i18n/gui/I18nMenu.java @@ -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 <bombe@freenetproject.org> + */ +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 index 0000000..753d581 --- /dev/null +++ b/src/net/pterodactylus/util/i18n/gui/package-info.java @@ -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 <bombe@freenetproject.org> + */ + +package net.pterodactylus.util.i18n.gui; \ No newline at end of file -- 2.7.4