Commit 900b1fb6 authored by Derek DeMoro's avatar Derek DeMoro Committed by derek

Added the ability to use i18n with plugins. Please refer to the...

Added the ability to use i18n with plugins. Please refer to the plugin-dev-guide.html for the how-tos.

git-svn-id: http://svn.igniterealtime.org/svn/repos/wildfire/trunk@3861 b35dd754-fafc-0310-a699-88a17e54d16e
parent a7e65a2f
...@@ -37,6 +37,7 @@ plugin directory are as follows: ...@@ -37,6 +37,7 @@ plugin directory are as follows:
|- icon_large.gif <- Optional large (32x32) icon associated with the plugin (can also be a .png file) |- icon_large.gif <- Optional large (32x32) icon associated with the plugin (can also be a .png file)
|- classes/ <- Resources your plugin needs (i.e., a properties file) |- classes/ <- Resources your plugin needs (i.e., a properties file)
|- database/ <- Optional database schema files that your plugin needs |- database/ <- Optional database schema files that your plugin needs
|- i18n/ <- Optional i18n files to allow for internationalization of plugins.
|- lib/ <- Libraries (JAR files) your plugin needs |- lib/ <- Libraries (JAR files) your plugin needs
|- web <- Resources for Admin Console integration, if any |- web <- Resources for Admin Console integration, if any
|- WEB-INF/ |- WEB-INF/
...@@ -281,6 +282,16 @@ The following HTML snippet demonstrates a valid page: ...@@ -281,6 +282,16 @@ The following HTML snippet demonstrates a valid page:
</pre> </pre>
</fieldset> </fieldset>
<h4>Using i18n in your Admin Console Pages</h4>
<p>
To use i18n in your own plugin, you must do the following:
<ul>
<li>Add i18n directory to root directory of your plugin.</li>
<li>Add each resource file using the %name_of_plugin%_i18n "_" language ".properties" naming convention.</li>
<li>To use directly within your jsp, use the LocaleUtils class, and pass in the name of the plugin.<br/><b>Example:&nbsp;</b>&lt;%= LocaleUtils.getLocalizedString("my.title", "myplugin") %&gt;
</li>
</ul>
</p>
<h2>Using the Wildfire Build Script</h2> <h2>Using the Wildfire Build Script</h2>
<p> <p>
......
...@@ -11,8 +11,23 @@ ...@@ -11,8 +11,23 @@
package org.jivesoftware.util; package org.jivesoftware.util;
import java.text.*; import org.jivesoftware.wildfire.XMPPServer;
import java.util.*; import org.jivesoftware.wildfire.container.Plugin;
import org.jivesoftware.wildfire.container.PluginManager;
import java.text.DateFormat;
import java.text.Format;
import java.text.MessageFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.StringTokenizer;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
/** /**
...@@ -23,7 +38,7 @@ import java.util.concurrent.ConcurrentHashMap; ...@@ -23,7 +38,7 @@ import java.util.concurrent.ConcurrentHashMap;
public class LocaleUtils { public class LocaleUtils {
private static final Map<Locale, String[][]> timeZoneLists = private static final Map<Locale, String[][]> timeZoneLists =
new ConcurrentHashMap<Locale, String[][]>(); new ConcurrentHashMap<Locale, String[][]>();
// The basename to use for looking up the appropriate resource bundles // The basename to use for looking up the appropriate resource bundles
// TODO - extract this out into a test that grabs the resource name from JiveGlobals // TODO - extract this out into a test that grabs the resource name from JiveGlobals
...@@ -57,8 +72,8 @@ public class LocaleUtils { ...@@ -57,8 +72,8 @@ public class LocaleUtils {
} }
} }
locale = new Locale(language, locale = new Locale(language,
((country != null) ? country : ""), ((country != null) ? country : ""),
((variant != null) ? variant : "")); ((variant != null) ? variant : ""));
} }
return locale; return locale;
} }
...@@ -66,80 +81,80 @@ public class LocaleUtils { ...@@ -66,80 +81,80 @@ public class LocaleUtils {
// The list of supported timezone ids. The list tries to include all of the relevant // The list of supported timezone ids. The list tries to include all of the relevant
// time zones for the world without any extraneous zones. // time zones for the world without any extraneous zones.
private static String[] timeZoneIds = new String[]{"GMT", private static String[] timeZoneIds = new String[]{"GMT",
"Pacific/Apia", "Pacific/Apia",
"HST", "HST",
"AST", "AST",
"America/Los_Angeles", "America/Los_Angeles",
"America/Phoenix", "America/Phoenix",
"America/Mazatlan", "America/Mazatlan",
"America/Denver", "America/Denver",
"America/Belize", "America/Belize",
"America/Chicago", "America/Chicago",
"America/Mexico_City", "America/Mexico_City",
"America/Regina", "America/Regina",
"America/Bogota", "America/Bogota",
"America/New_York", "America/New_York",
"America/Indianapolis", "America/Indianapolis",
"America/Halifax", "America/Halifax",
"America/Caracas", "America/Caracas",
"America/Santiago", "America/Santiago",
"America/St_Johns", "America/St_Johns",
"America/Sao_Paulo", "America/Sao_Paulo",
"America/Buenos_Aires", "America/Buenos_Aires",
"America/Godthab", "America/Godthab",
"Atlantic/South_Georgia", "Atlantic/South_Georgia",
"Atlantic/Azores", "Atlantic/Azores",
"Atlantic/Cape_Verde", "Atlantic/Cape_Verde",
"Africa/Casablanca", "Africa/Casablanca",
"Europe/Dublin", "Europe/Dublin",
"Europe/Berlin", "Europe/Berlin",
"Europe/Belgrade", "Europe/Belgrade",
"Europe/Paris", "Europe/Paris",
"Europe/Warsaw", "Europe/Warsaw",
"ECT", "ECT",
"Europe/Athens", "Europe/Athens",
"Europe/Bucharest", "Europe/Bucharest",
"Africa/Cairo", "Africa/Cairo",
"Africa/Harare", "Africa/Harare",
"Europe/Helsinki", "Europe/Helsinki",
"Asia/Jerusalem", "Asia/Jerusalem",
"Asia/Baghdad", "Asia/Baghdad",
"Asia/Kuwait", "Asia/Kuwait",
"Europe/Moscow", "Europe/Moscow",
"Africa/Nairobi", "Africa/Nairobi",
"Asia/Tehran", "Asia/Tehran",
"Asia/Muscat", "Asia/Muscat",
"Asia/Baku", "Asia/Baku",
"Asia/Kabul", "Asia/Kabul",
"Asia/Yekaterinburg", "Asia/Yekaterinburg",
"Asia/Karachi", "Asia/Karachi",
"Asia/Calcutta", "Asia/Calcutta",
"Asia/Katmandu", "Asia/Katmandu",
"Asia/Almaty", "Asia/Almaty",
"Asia/Dhaka", "Asia/Dhaka",
"Asia/Colombo", "Asia/Colombo",
"Asia/Rangoon", "Asia/Rangoon",
"Asia/Bangkok", "Asia/Bangkok",
"Asia/Krasnoyarsk", "Asia/Krasnoyarsk",
"Asia/Hong_Kong", "Asia/Hong_Kong",
"Asia/Irkutsk", "Asia/Irkutsk",
"Asia/Kuala_Lumpur", "Asia/Kuala_Lumpur",
"Australia/Perth", "Australia/Perth",
"Asia/Taipei", "Asia/Taipei",
"Asia/Tokyo", "Asia/Tokyo",
"Asia/Seoul", "Asia/Seoul",
"Asia/Yakutsk", "Asia/Yakutsk",
"Australia/Adelaide", "Australia/Adelaide",
"Australia/Darwin", "Australia/Darwin",
"Australia/Brisbane", "Australia/Brisbane",
"Australia/Sydney", "Australia/Sydney",
"Pacific/Guam", "Pacific/Guam",
"Australia/Hobart", "Australia/Hobart",
"Asia/Vladivostok", "Asia/Vladivostok",
"Pacific/Noumea", "Pacific/Noumea",
"Pacific/Auckland", "Pacific/Auckland",
"Pacific/Fiji", "Pacific/Fiji",
"Pacific/Tongatapu" "Pacific/Tongatapu"
}; };
// A mapping from the supported timezone ids to friendly english names. // A mapping from the supported timezone ids to friendly english names.
...@@ -274,7 +289,7 @@ public class LocaleUtils { ...@@ -274,7 +289,7 @@ public class LocaleUtils {
// Add in the GMT part to the name. First, figure out the offset. // Add in the GMT part to the name. First, figure out the offset.
int offset = zone.getRawOffset(); int offset = zone.getRawOffset();
if (zone.inDaylightTime(new Date()) && zone.useDaylightTime()) { if (zone.inDaylightTime(new Date()) && zone.useDaylightTime()) {
offset += (int) JiveConstants.HOUR; offset += (int)JiveConstants.HOUR;
} }
buf.append("("); buf.append("(");
...@@ -285,8 +300,8 @@ public class LocaleUtils { ...@@ -285,8 +300,8 @@ public class LocaleUtils {
buf.append("GMT+"); buf.append("GMT+");
} }
offset = Math.abs(offset); offset = Math.abs(offset);
int hours = offset / (int) JiveConstants.HOUR; int hours = offset / (int)JiveConstants.HOUR;
int minutes = (offset % (int) JiveConstants.HOUR) / (int) JiveConstants.MINUTE; int minutes = (offset % (int)JiveConstants.HOUR) / (int)JiveConstants.MINUTE;
buf.append(hours).append(":"); buf.append(hours).append(":");
if (minutes < 10) { if (minutes < 10) {
buf.append("0").append(minutes); buf.append("0").append(minutes);
...@@ -307,8 +322,8 @@ public class LocaleUtils { ...@@ -307,8 +322,8 @@ public class LocaleUtils {
} }
else { else {
buf.append( buf.append(
zone.getDisplayName(true, TimeZone.LONG, locale).replace('_', ' ').replace('/', zone.getDisplayName(true, TimeZone.LONG, locale).replace('_', ' ').replace('/',
' ')); ' '));
} }
return buf.toString(); return buf.toString();
...@@ -338,7 +353,11 @@ public class LocaleUtils { ...@@ -338,7 +353,11 @@ public class LocaleUtils {
* @return the localized string. * @return the localized string.
*/ */
public static String getLocalizedString(String key) { public static String getLocalizedString(String key) {
return getLocalizedString(key, JiveGlobals.getLocale(), null); Locale locale = JiveGlobals.getLocale();
ResourceBundle bundle = ResourceBundle.getBundle(resourceBaseName, locale);
return getLocalizedString(key, locale, null, bundle);
} }
/** /**
...@@ -352,7 +371,9 @@ public class LocaleUtils { ...@@ -352,7 +371,9 @@ public class LocaleUtils {
* @return the localized string. * @return the localized string.
*/ */
public static String getLocalizedString(String key, Locale locale) { public static String getLocalizedString(String key, Locale locale) {
return getLocalizedString(key, locale, null); ResourceBundle bundle = ResourceBundle.getBundle(resourceBaseName, locale);
return getLocalizedString(key, locale, null, bundle);
} }
/** /**
...@@ -368,23 +389,68 @@ public class LocaleUtils { ...@@ -368,23 +389,68 @@ public class LocaleUtils {
* @return the localized string. * @return the localized string.
*/ */
public static String getLocalizedString(String key, List arguments) { public static String getLocalizedString(String key, List arguments) {
return getLocalizedString(key, JiveGlobals.getLocale(), arguments); Locale locale = JiveGlobals.getLocale();
ResourceBundle bundle = ResourceBundle.getBundle(resourceBaseName, locale);
return getLocalizedString(key, locale, arguments, bundle);
}
/**
* Returns an internationalized string loaded from a resource bundle from the passed
* in plugin.
*
* @param key the key to use for retrieving the string from the
* appropriate resource bundle.
* @param pluginName the name of the plugin to load the require resource bundle from.
* @return the localized string.
*/
public static String getLocalizedString(String key, String pluginName) {
return getLocalizedString(key, pluginName, null);
}
/**
* Returns an internationalized string loaded from a resource bundle from the passed
* in plugin.
*
* @param key the key to use for retrieving the string from the
* appropriate resource bundle.
* @param pluginName the name of the plugin to load the require resource bundle from.
* @param arguments a list of objects to use which are formatted, then
* inserted into the pattern at the appropriate places.
* @return the localized string.
*/
public static String getLocalizedString(String key, String pluginName, List arguments) {
Locale locale = JiveGlobals.getLocale();
String i18nFile = pluginName + "_i18n";
// Retrieve classloader from pluginName.
final XMPPServer xmppServer = XMPPServer.getInstance();
PluginManager pluginManager = xmppServer.getPluginManager();
Plugin plugin = pluginManager.getPlugin(pluginName);
if(plugin == null){
throw new NullPointerException("Plugin could not be located.");
}
ClassLoader pluginClassLoader = pluginManager.getPluginClassloader(plugin).getClassLoader();
ResourceBundle bundle = ResourceBundle.getBundle(i18nFile, locale, pluginClassLoader);
return getLocalizedString(key, locale, arguments, bundle);
} }
/** /**
* Returns an internationalized string loaded from a resource bundle using * Returns an internationalized string loaded from a resource bundle using
* the passed in Locale substituting the passed in arguments. Substitution * the passed in Locale substituting the passed in arguments. Substitution
* is handled using the {@link java.text.MessageFormat} class. * is handled using the {@link MessageFormat} class.
* *
* @param key the key to use for retrieving the string from the * @param key the key to use for retrieving the string from the
* appropriate resource bundle. * appropriate resource bundle.
* @param locale the locale to use for retrieving the appropriate * @param locale the locale to use for retrieving the appropriate
* locale-specific string. * locale-specific string.
* @param arguments a list of objects to use which are formatted, then * @param arguments a list of objects to use which are formatted, then
* inserted into the pattern at the appropriate places. * inserted into the pattern at the appropriate places.
* @return the localized string. * @return the localized string.
*/ */
public static String getLocalizedString(String key, Locale locale, List arguments) { public static String getLocalizedString(String key, Locale locale, List arguments, ResourceBundle bundle) {
if (key == null) { if (key == null) {
throw new NullPointerException("Key cannot be null"); throw new NullPointerException("Key cannot be null");
} }
...@@ -397,7 +463,6 @@ public class LocaleUtils { ...@@ -397,7 +463,6 @@ public class LocaleUtils {
// See if the bundle has a value // See if the bundle has a value
try { try {
// The jdk caches resource bundles on it's own, so we won't bother. // The jdk caches resource bundles on it's own, so we won't bother.
ResourceBundle bundle = ResourceBundle.getBundle(resourceBaseName, locale);
value = bundle.getString(key); value = bundle.getString(key);
// perform argument substitutions // perform argument substitutions
if (arguments != null) { if (arguments != null) {
...@@ -449,14 +514,14 @@ public class LocaleUtils { ...@@ -449,14 +514,14 @@ public class LocaleUtils {
} }
catch (IllegalArgumentException e) { catch (IllegalArgumentException e) {
Log.error("Unable to format resource string for key: " Log.error("Unable to format resource string for key: "
+ key + ", argument type not supported"); + key + ", argument type not supported");
value = ""; value = "";
} }
} }
} }
catch (java.util.MissingResourceException mre) { catch (java.util.MissingResourceException mre) {
Log.warn("Missing resource for key: " + key Log.warn("Missing resource for key: " + key
+ " in locale " + locale.toString()); + " in locale " + locale.toString());
value = ""; value = "";
} }
......
...@@ -34,7 +34,7 @@ import java.util.List; ...@@ -34,7 +34,7 @@ import java.util.List;
* *
* @author Derek DeMoro * @author Derek DeMoro
*/ */
class PluginClassLoader { public class PluginClassLoader {
private URLClassLoader classLoader; private URLClassLoader classLoader;
private final List<URL> list = new ArrayList<URL>(); private final List<URL> list = new ArrayList<URL>();
...@@ -58,10 +58,19 @@ class PluginClassLoader { ...@@ -58,10 +58,19 @@ class PluginClassLoader {
*/ */
public void addDirectory(File directory, boolean developmentMode) { public void addDirectory(File directory, boolean developmentMode) {
try { try {
// Add classes directory to classpath.
File classesDir = new File(directory, "classes"); File classesDir = new File(directory, "classes");
if (classesDir.exists()) { if (classesDir.exists()) {
list.add(classesDir.toURL()); list.add(classesDir.toURL());
} }
// Add i18n directory to classpath.
File i18nDir = new File(directory, "i18n");
if(i18nDir.exists()){
list.add(i18nDir.toURL());
}
// Add lib directory to classpath.
File libDir = new File(directory, "lib"); File libDir = new File(directory, "lib");
File[] jars = libDir.listFiles(new FilenameFilter() { File[] jars = libDir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) { public boolean accept(File dir, String name) {
...@@ -161,4 +170,12 @@ class PluginClassLoader { ...@@ -161,4 +170,12 @@ class PluginClassLoader {
} }
return parent; return parent;
} }
/**
* Returns the URLClassloader used.
* @return the URLClassLoader used.
*/
public ClassLoader getClassLoader(){
return classLoader;
}
} }
...@@ -167,16 +167,14 @@ ...@@ -167,16 +167,14 @@
} }
else { else {
errorBuf.append("<br>").append( errorBuf.append("<br>").append(
LocaleUtils.getLocalizedString("group.edit.already_user", LocaleUtils.getLocalizedString("group.edit.already_user", Arrays.asList(username)));
JiveGlobals.getLocale(), Arrays.asList(username)));
} }
} }
catch (Exception e) { catch (Exception e) {
Log.debug("Problem adding new user to existing group", e); Log.debug("Problem adding new user to existing group", e);
errorBuf.append("<br>").append( errorBuf.append("<br>").append(
LocaleUtils.getLocalizedString("group.edit.inexistent_user", LocaleUtils.getLocalizedString("group.edit.inexistent_user", Arrays.asList(username)));
JiveGlobals.getLocale(), Arrays.asList(username)));
} }
} }
if (count > 0) { if (count > 0) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment