You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@turbine.apache.org by ep...@apache.org on 2003/08/16 04:58:50 UTC
cvs commit: jakarta-turbine-fulcrum/localization/src/test TestComponentConfig.xml TestRoleConfig.xml
epugh 2003/08/15 19:58:50
Added: localization/src/java/org/apache/fulcrum/localization
LocaleTokenizer.java LocalizationService.java
DefaultLocalizationService.java
localization/src/test/org/apache/fulcrum/localization
LocaleTokenizerTest.java BarBundle_en_US.java
FooBundle_en.java BarBundle_ko_KR.java
LocalizationTest.java FooBundle_fr.java
localization/src/test TestComponentConfig.xml
TestRoleConfig.xml
Log:
adding localization sub project
Revision Changes Path
1.1 jakarta-turbine-fulcrum/localization/src/java/org/apache/fulcrum/localization/LocaleTokenizer.java
Index: LocaleTokenizer.java
===================================================================
package org.apache.fulcrum.localization;
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache Turbine" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache Turbine", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.Locale;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
/**
* Parses the HTTP <code>Accept-Language</code> header as per section
* 14.4 of RFC 2068 (HTTP 1.1 header field definitions).
*
* @author <a href="mailto:dlr@collab.net">Daniel Rall</a>
* @version $Id: LocaleTokenizer.java,v 1.1 2003/08/16 02:58:50 epugh Exp $
*/
public class LocaleTokenizer
implements Iterator
{
/**
* Separates elements of the <code>Accept-Language</code> HTTP
* header.
*/
private static final String LOCALE_SEPARATOR = ",";
/**
* Separates locale from quality within elements.
*/
private static final char QUALITY_SEPARATOR = ';';
/**
* The default quality value for an <code>AcceptLanguage</code>
* object.
*/
private static final Float DEFAULT_QUALITY = new Float(1.0f);
/**
* The parsed locales.
*/
private ArrayList locales = new ArrayList(3);
/**
* Parses the <code>Accept-Language</code> header.
*
* @param header The <code>Accept-Language</code> header
* (i.e. <code>en, es;q=0.8, zh-TW;q=0.1</code>).
*/
public LocaleTokenizer(String header)
{
StringTokenizer tok = new StringTokenizer(header, LOCALE_SEPARATOR);
while (tok.hasMoreTokens())
{
AcceptLanguage acceptLang = new AcceptLanguage();
String element = tok.nextToken().trim();
int index;
// Record and cut off any quality value that comes after a
// semi-colon.
if ( (index = element.indexOf(QUALITY_SEPARATOR)) != -1 )
{
String q = element.substring(index);
element = element.substring(0, index);
if ( (index = q.indexOf('=')) != -1 )
{
try
{
acceptLang.quality =
Float.valueOf(q.substring(index + 1));
}
catch (NumberFormatException useDefault)
{
}
}
}
element = element.trim();
// Create a Locale from the language. A dash may separate the
// language from the country.
if ( (index = element.indexOf('-')) == -1 )
{
// No dash means no country.
acceptLang.locale = new Locale(element, "");
}
else
{
acceptLang.locale = new Locale(element.substring(0, index),
element.substring(index + 1));
}
locales.add(acceptLang);
}
// Sort by quality in descending order.
Collections.sort(locales, Collections.reverseOrder());
}
/**
* @return Whether there are more locales.
*/
public boolean hasNext()
{
return !locales.isEmpty();
}
/**
* Creates a <code>Locale</code> from the next element of the
* <code>Accept-Language</code> header.
*
* @return The next highest-rated <code>Locale</code>.
* @throws NoSuchElementException No more locales.
*/
public Object next()
{
if (locales.isEmpty())
{
throw new NoSuchElementException();
}
return ((AcceptLanguage) locales.remove(0)).locale;
}
/**
* Not implemented.
*/
public final void remove()
{
throw new UnsupportedOperationException(getClass().getName() +
" does not support remove()");
}
/**
* Struct representing an element of the HTTP
* <code>Accept-Language</code> header.
*/
private class AcceptLanguage implements Comparable
{
/**
* The language and country.
*/
Locale locale;
/**
* The quality of our locale (as values approach
* <code>1.0</code>, they indicate increased user preference).
*/
Float quality = DEFAULT_QUALITY;
public final int compareTo(Object acceptLang)
{
return quality.compareTo( ((AcceptLanguage) acceptLang).quality );
}
}
}
1.1 jakarta-turbine-fulcrum/localization/src/java/org/apache/fulcrum/localization/LocalizationService.java
Index: LocalizationService.java
===================================================================
package org.apache.fulcrum.localization;
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache Turbine" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache Turbine", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
import java.util.Locale;
import java.util.ResourceBundle;
import javax.servlet.http.HttpServletRequest;
import org.apache.avalon.framework.component.Component;
/**
* <p>Provides localization functionality using the interface provided
* by <code>ResourceBundle</code>, plus leverages a "search path"
* style traversal of the <code>ResourceBundle</code> objects named by
* the <code>locale.default.bundles</code> to discover a value for a
* given key.</p>
*
* <p>It is suggested that one handle
* <a href="http://www.math.fu-berlin.de/~rene/www/java/tutorial/i18n/message/messageFormat.html">dealing with concatenated messages</a>
* using <code>MessageFormat</code> and properties files.</p>
*
* @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
* @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
* @author <a href="mailto:leonardr@collab.net">Leonard Richardson</a>
* @version $Id: LocalizationService.java,v 1.1 2003/08/16 02:58:50 epugh Exp $
*/
public interface LocalizationService
extends Component
{
String ROLE = LocalizationService.class.getName();
String SERVICE_NAME = ROLE;
/**
* A constant for the HTTP <code>Accept-Language</code> header.
*/
String ACCEPT_LANGUAGE = "Accept-Language";
/**
* Retrieves the default language (as specified in the config
* file).
*/
String getDefaultLanguage();
/**
* Retrieves the default country (as specified in the config
* file).
*/
String getDefaultCountry();
/**
* Retrieves the name of the default bundle (as specified in the
* config file), or the first in the list if there are more than
* one.
*/
String getDefaultBundleName();
/**
* Retrieves the list of names of bundles to search by default for
* <code>ResourceBundle</code> keys (as specified in the config
* file).
*
* @return The list of configured bundle names.
*/
String[] getBundleNames();
/**
* Convenience method to get the default <code>ResourceBundle</code>.
*
* @return A localized <code>ResourceBundle</code>.
*/
ResourceBundle getBundle();
/**
* Returns a ResourceBundle given the bundle name and the default
* locale information supplied by the configuration.
*
* @param bundleName Name of bundle.
* @return A localized ResourceBundle.
*/
ResourceBundle getBundle(String bundleName);
/**
* Convenience method to get a ResourceBundle based on name and
* HTTP <code>Accept-Language</code> header.
*
* @param bundleName Name of bundle.
* @param languageHeader A String with the language header.
* @return A localized ResourceBundle.
*/
ResourceBundle getBundle(String bundleName, String languageHeader);
/**
* Convenience method to get a ResourceBundle based on HTTP
* Accept-Language header in HttpServletRequest.
*
* @param req The HTTP request to parse the
* <code>Accept-Language</code> of.
* @return A localized ResourceBundle.
*/
ResourceBundle getBundle(HttpServletRequest req);
/**
* Convenience method to get a <code>ResourceBundle</code> based
* on name and HTTP <code>Accept-Language</code> header from a
* <code>HttpServletRequest</code>.
*
* @param bundleName Name of bundle.
* @param req The HTTP request to parse the
* <code>Accept-Language</code> of.
* @return A localized ResourceBundle.
*/
ResourceBundle getBundle(String bundleName, HttpServletRequest req);
/**
* Convenience method to get a ResourceBundle based on name and
* Locale.
*
* @param bundleName Name of bundle.
* @param locale A Locale.
* @return A localized ResourceBundle.
*/
ResourceBundle getBundle(String bundleName, Locale locale);
/**
* Attempts to pull the <code>Accept-Language</code> header out of
* the <code>HttpServletRequest</code> object and then parse it.
* If the header is not present, it will return a
* <code>null</code> <code>Locale</code>.
*
* @param req The HTTP request to parse the
* <code>Accept-Language</code> of.
* @return The parsed locale.
*/
Locale getLocale(HttpServletRequest req);
/**
* Parses the <code>Accept-Language</code> header and attempts to
* create a <code>Locale</code> from it.
*
* @param header The language header (i.e. <code>en, es;q=0.8,
* zh-TW;q=0.1</code>), or <code>null</code> for the locale
* corresponding to the default language and country.
* @return The parsed locale, or a locale corresponding to the
* language and country defaults.
*/
Locale getLocale(String languageHeader);
/**
* Tries very hard to return a value, looking first in the
* specified bundle, then searching list of default bundles
* (giving precedence to earlier bundles over later bundles).
*
* @param bundleName Name of the bundle to look in first.
* @param locale Locale to get text for.
* @param key Name of the text to retrieve.
* @return Localized text.
*/
String getString(String bundleName, Locale locale, String key);
/**
* This method sets the name of the defaultBundle.
*
* @param defaultBundle Name of default bundle.
*/
void setBundle(String defaultBundle);
/**
* Formats a localized value using the provided object.
*
* @param bundleName The bundle in which to look for the localizable text.
* @param locale The locale for which to format the text.
* @param key The identifier for the localized text to retrieve,
* @param arg1 The object to use as {0} when formatting the localized text.
* @return Formatted localized text.
* @see #format(String, Locale, String, Object[])
*/
String format(String bundleName, Locale locale,
String key, Object arg1);
/**
* Formats a localized value using the provided objects.
*
* @param bundleName The bundle in which to look for the localizable text.
* @param locale The locale for which to format the text.
* @param key The identifier for the localized text to retrieve,
* @param arg1 The object to use as {0} when formatting the localized text.
* @param arg2 The object to use as {1} when formatting the localized text.
* @return Formatted localized text.
* @see #format(String, Locale, String, Object[])
*/
String format(String bundleName, Locale locale,
String key, Object arg1, Object arg2);
/**
* Formats a localized value using the provided objects.
*
* @param bundleName The bundle in which to look for the localizable text.
* @param locale The locale for which to format the text.
* @param key The identifier for the localized text to retrieve,
* @param args The objects to use as {0}, {1}, etc. when
* formatting the localized text.
* @return Formatted localized text.
*/
String format(String bundleName, Locale locale,
String key, Object[] args);
}
1.1 jakarta-turbine-fulcrum/localization/src/java/org/apache/fulcrum/localization/DefaultLocalizationService.java
Index: DefaultLocalizationService.java
===================================================================
package org.apache.fulcrum.localization;
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache Turbine" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache Turbine", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Category;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.thread.ThreadSafe;
/**
* <p>This class is the single point of access to all localization
* resources. It caches different ResourceBundles for different
* Locales.</p>
*
* <p>Usage example:</p>
*
* <blockquote><code><pre>
* LocalizationService ls = (LocalizationService) TurbineServices
* .getInstance().getService(LocalizationService.SERVICE_NAME);
* </pre></code></blockquote>
*
* <p>Then call {@link #getString(String, Locale, String)}, or one of
* four methods to retrieve a ResourceBundle:
*
* <ul>
* <li>getBundle("MyBundleName")</li>
* <li>getBundle("MyBundleName", httpAcceptLanguageHeader)</li>
* <li>etBundle("MyBundleName", HttpServletRequest)</li>
* <li>getBundle("MyBundleName", Locale)</li>
* <li>etc.</li>
* </ul></p>
*
* @author <a href="mailto:jm@mediaphil.de">Jonas Maurus</a>
* @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
* @author <a href="mailto:novalidemail@foo.com">Frank Y. Kim</a>
* @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
* @author <a href="mailto:leonardr@collab.net">Leonard Richardson</a>
* @version $Id: DefaultLocalizationService.java,v 1.1 2003/08/16 02:58:50 epugh Exp $
*/
public class DefaultLocalizationService
extends AbstractLogEnabled
implements LocalizationService, Configurable, Initializable, ThreadSafe
{
/** Key Prefix for our bundles */
private static final String BUNDLES = "bundles";
/**
* The value to pass to <code>MessageFormat</code> if a
* <code>null</code> reference is passed to <code>format()</code>.
*/
private static final Object[] NO_ARGS = new Object[0];
/**
* Bundle name keys a HashMap of the ResourceBundles in this
* service (which is in turn keyed by Locale).
*/
private HashMap bundles = null;
/**
* The list of default bundles to search.
*/
private String[] bundleNames = null;
/**
* The default bundle name to use if not specified.
*/
private String defaultBundleName = null;
/**
* The name of the default locale to use (includes language and
* country).
*/
private Locale defaultLocale = null;
/** The name of the default language to use. */
private String defaultLanguage;
/** The name of the default country to use. */
private String defaultCountry = null;
/**
* Log4J logging category.
*/
private Category category = Category.getInstance(getClass().getName());
/**
* Creates a new instance.
*/
public DefaultLocalizationService()
{
bundles = new HashMap();
}
public void configure(Configuration conf) throws ConfigurationException
{
Locale jvmDefault = Locale.getDefault();
System.out.println("Using parameters");
defaultLanguage = conf.getAttribute("locale-default-language", jvmDefault.getLanguage()).trim();
defaultCountry = conf.getAttribute("locale-default-country", jvmDefault.getCountry()).trim();
// FIXME! need to add bundle names
getLogger().info("initialized lang=" + defaultLanguage + " country=" + defaultCountry);
final Configuration bundles = conf.getChild(BUNDLES, false);
if (bundles != null)
{
Configuration[] nameVal = bundles.getChildren();
String bundleName[] = new String[nameVal.length];
for (int i = 0; i < nameVal.length; i++)
{
String key = nameVal[i].getName();
String val = nameVal[i].getValue();
getLogger().debug("Registered bundle " + val);
bundleName[i] = val;
}
initBundleNames(bundleName);
}
}
/**
* Called the first time the Service is used.
*/
public void initialize() throws Exception
{
// initBundleNames(null);
defaultLocale = new Locale(defaultLanguage, defaultCountry);
}
/**
* Initialize list of default bundle names.
*
* @param ignored names Ignored.
*/
protected void initBundleNames(String[] intBundleNames)
{
//System.err.println("cfg=" + getConfiguration());
if (defaultBundleName != null && defaultBundleName.length() > 0)
{
// Using old-style single bundle name property.
if (intBundleNames == null || intBundleNames.length <= 0)
{
bundleNames = new String[] { defaultBundleName };
}
else
{
// Prepend "default" bundle name.
String[] array = new String[intBundleNames.length + 1];
array[0] = defaultBundleName;
System.arraycopy(intBundleNames, 0, array, 1, intBundleNames.length);
bundleNames = array;
}
}
if (intBundleNames == null)
{
bundleNames = new String[0];
}
bundleNames = intBundleNames;
}
/**
* Retrieves the default language (specified in the config file).
*/
public String getDefaultLanguage()
{
return defaultLanguage;
}
/**
* Retrieves the default country (specified in the config file).
*/
public String getDefaultCountry()
{
return defaultCountry;
}
/**
* @see org.apache.fulcrum.localization.LocalizationService#getDefaultBundleName()
*/
public String getDefaultBundleName()
{
return (bundleNames.length > 0 ? bundleNames[0] : "");
}
/**
* @see org.apache.fulcrum.localization.LocalizationService#getBundleNames()
*/
public String[] getBundleNames()
{
return (String[]) bundleNames.clone();
}
/**
* @see org.apache.fulcrum.localization.LocalizationService#getBundle()
*/
public ResourceBundle getBundle()
{
return getBundle(getDefaultBundleName(), (Locale) null);
}
/**
* @see org.apache.fulcrum.localization.LocalizationService#getBundle(String)
*/
public ResourceBundle getBundle(String bundleName)
{
return getBundle(bundleName, (Locale) null);
}
/**
* This method returns a ResourceBundle given the bundle name and
* the Locale information supplied in the HTTP "Accept-Language"
* header.
*
* @param bundleName Name of bundle.
* @param languageHeader A String with the language header.
* @return A localized ResourceBundle.
*/
public ResourceBundle getBundle(String bundleName, String languageHeader)
{
return getBundle(bundleName, getLocale(languageHeader));
}
/**
* This method returns a ResourceBundle given the Locale
* information supplied in the HTTP "Accept-Language" header which
* is stored in HttpServletRequest.
*
* @param req HttpServletRequest.
* @return A localized ResourceBundle.
*/
public ResourceBundle getBundle(HttpServletRequest req)
{
return getBundle(getDefaultBundleName(), getLocale(req));
}
/**
* This method returns a ResourceBundle given the bundle name and
* the Locale information supplied in the HTTP "Accept-Language"
* header which is stored in HttpServletRequest.
*
* @param bundleName Name of the bundle to use if the request's
* locale cannot be resolved.
* @param req HttpServletRequest.
* @return A localized ResourceBundle.
*/
public ResourceBundle getBundle(String bundleName, HttpServletRequest req)
{
return getBundle(bundleName, getLocale(req));
}
/**
* This method returns a ResourceBundle for the given bundle name
* and the given Locale.
*
* @param bundleName Name of bundle (or <code>null</code> for the
* default bundle).
* @param locale The locale (or <code>null</code> for the locale
* indicated by the default language and country).
* @return A localized ResourceBundle.
*/
public ResourceBundle getBundle(String bundleName, Locale locale)
{
// Assure usable inputs.
bundleName = (bundleName == null ? getDefaultBundleName() : bundleName.trim());
if (locale == null)
{
locale = getLocale((String) null);
}
// Find/retrieve/cache bundle.
ResourceBundle rb = null;
HashMap bundlesByLocale = (HashMap) bundles.get(bundleName);
if (bundlesByLocale != null)
{
// Cache of bundles by locale for the named bundle exists.
// Check the cache for a bundle corresponding to locale.
rb = (ResourceBundle) bundlesByLocale.get(locale);
if (rb == null)
{
// Not yet cached.
rb = cacheBundle(bundleName, locale);
}
}
else
{
rb = cacheBundle(bundleName, locale);
}
return rb;
}
/**
* Caches the named bundle for fast lookups. This operation is
* relatively expesive in terms of memory use, but is optimized
* for run-time speed in the usual case.
*
* @exception MissingResourceException Bundle not found.
*/
private synchronized ResourceBundle cacheBundle(String bundleName, Locale locale) throws MissingResourceException
{
HashMap bundlesByLocale = (HashMap) bundles.get(bundleName);
ResourceBundle rb = (bundlesByLocale == null ? null : (ResourceBundle) bundlesByLocale.get(locale));
if (rb == null)
{
bundlesByLocale = (bundlesByLocale == null ? new HashMap(3) : new HashMap(bundlesByLocale));
try
{
rb = ResourceBundle.getBundle(bundleName, locale);
}
catch (MissingResourceException e)
{
rb = findBundleByLocale(bundleName, locale, bundlesByLocale);
if (rb == null)
{
throw (MissingResourceException) e.fillInStackTrace();
}
}
if (rb != null)
{
// Cache bundle.
bundlesByLocale.put(rb.getLocale(), rb);
HashMap bundlesByName = new HashMap(bundles);
bundlesByName.put(bundleName, bundlesByLocale);
this.bundles = bundlesByName;
}
}
return rb;
}
/**
* <p>Retrieves the bundle most closely matching first against the
* supplied inputs, then against the defaults.</p>
*
* <p>Use case: some clients send a HTTP Accept-Language header
* with a value of only the language to use
* (i.e. "Accept-Language: en"), and neglect to include a country.
* When there is no bundle for the requested language, this method
* can be called to try the default country (checking internally
* to assure the requested criteria matches the default to avoid
* disconnects between language and country).</p>
*
* <p>Since we're really just guessing at possible bundles to use,
* we don't ever throw <code>MissingResourceException</code>.</p>
*/
private ResourceBundle findBundleByLocale(String bundleName, Locale locale, Map bundlesByLocale)
{
ResourceBundle rb = null;
if (!StringUtils.isNotEmpty(locale.getCountry()) && defaultLanguage.equals(locale.getLanguage()))
{
/*
category.debug("Requested language '" + locale.getLanguage() +
"' matches default: Attempting to guess bundle " +
"using default country '" + defaultCountry + '\'');
*/
Locale withDefaultCountry = new Locale(locale.getLanguage(), defaultCountry);
rb = (ResourceBundle) bundlesByLocale.get(withDefaultCountry);
if (rb == null)
{
rb = getBundleIgnoreException(bundleName, withDefaultCountry);
}
}
else if (!StringUtils.isNotEmpty(locale.getLanguage()) && defaultCountry.equals(locale.getCountry()))
{
Locale withDefaultLanguage = new Locale(defaultLanguage, locale.getCountry());
rb = (ResourceBundle) bundlesByLocale.get(withDefaultLanguage);
if (rb == null)
{
rb = getBundleIgnoreException(bundleName, withDefaultLanguage);
}
}
if (rb == null && !defaultLocale.equals(locale))
{
rb = getBundleIgnoreException(bundleName, defaultLocale);
}
return rb;
}
/**
* Retrieves the bundle using the
* <code>ResourceBundle.getBundle(String, Locale)</code> method,
* returning <code>null</code> instead of throwing
* <code>MissingResourceException</code>.
*/
private final ResourceBundle getBundleIgnoreException(String bundleName, Locale locale)
{
try
{
return ResourceBundle.getBundle(bundleName, locale);
}
catch (MissingResourceException ignored)
{
return null;
}
}
/**
* This method sets the name of the first bundle in the search
* list (the "default" bundle).
*
* @param defaultBundle Name of default bundle.
*/
public void setBundle(String defaultBundle)
{
if (bundleNames.length > 0)
{
bundleNames[0] = defaultBundle;
}
else
{
synchronized (this)
{
if (bundleNames.length <= 0)
{
bundleNames = new String[] { defaultBundle };
}
}
}
}
/**
* @see org.apache.fulcrum.localization.LocalizationService#getLocale(HttpServletRequest)
*/
public Locale getLocale(HttpServletRequest req)
{
return getLocale(req.getHeader(ACCEPT_LANGUAGE));
// (JSS) Backed out this change because Tomcat seems to be returning
// the wrong result and things just are not working.
// Locale l = req.getLocale();
// return (l != null ? l : getLocale(req.getHeader(ACCEPT_LANGUAGE)));
}
/**
* @see org.apache.fulcrum.localization.LocalizationService#getLocale(String)
*/
public Locale getLocale(String header)
{
if (!StringUtils.isEmpty(header))
{
LocaleTokenizer tok = new LocaleTokenizer(header);
if (tok.hasNext())
{
return (Locale) tok.next();
}
}
// Couldn't parse locale.
return defaultLocale;
}
/**
* @exception MissingResourceException Specified key cannot be matched.
* @see org.apache.fulcrum.localization.LocalizationService#getString(String, Locale, String)
*/
public String getString(String bundleName, Locale locale, String key)
{
String value = null;
if (locale == null)
{
locale = getLocale((String) null);
}
// Look for text in requested bundle.
ResourceBundle rb = getBundle(bundleName, locale);
value = getStringOrNull(rb, key);
// Look for text in list of default bundles.
if (value == null && bundleNames.length > 0)
{
String name;
for (int i = 0; i < bundleNames.length; i++)
{
name = bundleNames[i];
//System.out.println("getString(): name=" + name +
// ", locale=" + locale + ", i=" + i);
if (!name.equals(bundleName))
{
rb = getBundle(name, locale);
value = getStringOrNull(rb, key);
if (value != null)
{
locale = rb.getLocale();
break;
}
}
}
}
if (value == null)
{
String loc = locale.toString();
String mesg =
LocalizationService.SERVICE_NAME
+ " noticed missing resource: "
+ "bundleName="
+ bundleName
+ ", locale="
+ loc
+ ", key="
+ key;
category.debug(mesg);
// Text not found in requested or default bundles.
throw new MissingResourceException(mesg, bundleName, key);
}
return value;
}
/**
* Gets localized text from a bundle if it's there. Otherwise,
* returns <code>null</code> (ignoring a possible
* <code>MissingResourceException</code>).
*/
protected final String getStringOrNull(ResourceBundle rb, String key)
{
if (rb != null)
{
try
{
return rb.getString(key);
}
catch (MissingResourceException ignored)
{
}
}
return null;
}
/**
* @see org.apache.fulcrum.localization.LocalizationService#format(String, Locale, String, Object)
*/
public String format(String bundleName, Locale locale, String key, Object arg1)
{
return format(bundleName, locale, key, new Object[] { arg1 });
}
/**
* @see org.apache.fulcrum.localization.LocalizationService#format(String, Locale, String, Object, Object)
*/
public String format(String bundleName, Locale locale, String key, Object arg1, Object arg2)
{
return format(bundleName, locale, key, new Object[] { arg1, arg2 });
}
/**
* Looks up the value for <code>key</code> in the
* <code>ResourceBundle</code> referenced by
* <code>bundleName</code>, then formats that value for the
* specified <code>Locale</code> using <code>args</code>.
*
* @return Localized, formatted text identified by
* <code>key</code>.
*/
public String format(String bundleName, Locale locale, String key, Object[] args)
{
if (locale == null)
{
// When formatting Date objects and such, MessageFormat
// cannot have a null Locale.
locale = getLocale((String) null);
}
String value = getString(bundleName, locale, key);
if (args == null)
{
args = NO_ARGS;
}
// FIXME: after switching to JDK 1.4, it will be possible to clean
// this up by providing the Locale along with the string in the
// constructor to MessageFormat. Until 1.4, the following workaround
// is required for constructing the format with the appropriate locale:
MessageFormat messageFormat = new MessageFormat("");
messageFormat.setLocale(locale);
messageFormat.applyPattern(value);
return messageFormat.format(args);
}
/**
* The name used to specify this component in TurbineResources.properties
* @deprecated part of the pre-avalon compatibility layer
*/
protected String getName()
{
return "LocalizationService";
}
}
1.1 jakarta-turbine-fulcrum/localization/src/test/org/apache/fulcrum/localization/LocaleTokenizerTest.java
Index: LocaleTokenizerTest.java
===================================================================
package org.apache.fulcrum.localization;
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache Turbine" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache Turbine", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
import java.util.Locale;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
* Test case for the locale tokenizer.
*
* @author <a href="mailto:dlr@collab.net">Daniel Rall</a>
* @author <a href="mailto:jason@zenplex.com">Jason van Zyl</a>
* @version $Id: LocaleTokenizerTest.java,v 1.1 2003/08/16 02:58:50 epugh Exp $
*/
public class LocaleTokenizerTest
extends TestCase
{
private static final String HEADER = "en, es;q=0.8, zh-TW;q=0.1";
public LocaleTokenizerTest(String name)
{
super(name);
}
public static Test suite()
{
return new TestSuite(LocaleTokenizerTest.class);
}
public void testLocaleTokenizer()
{
try
{
LocaleTokenizer tok = new LocaleTokenizer(HEADER);
Locale locale = (Locale) tok.next();
assertEquals("Either wrong language or order parsing: " + locale,
locale.getLanguage(), "en");
locale = (Locale) tok.next();
assertEquals("Either wrong language or order parsing: " + locale,
locale.getLanguage(), "es");
locale = (Locale) tok.next();
assertEquals("Either wrong country or order parsing: " + locale,
locale.getCountry(), "TW");
}
catch (Exception e)
{
e.printStackTrace();
fail();
}
}
}
1.1 jakarta-turbine-fulcrum/localization/src/test/org/apache/fulcrum/localization/BarBundle_en_US.java
Index: BarBundle_en_US.java
===================================================================
package org.apache.fulcrum.localization;
import java.util.ListResourceBundle;
/**
* An english resource bundle for use in testing.
*/
public class BarBundle_en_US extends ListResourceBundle
{
private static final Object[][] CONTENTS =
{
{ "key1", "value1" },
{ "key2", "value2" },
{ "key3", "value3" },
{ "key4", "value4" }
};
protected Object[][] getContents()
{
return CONTENTS;
}
}
1.1 jakarta-turbine-fulcrum/localization/src/test/org/apache/fulcrum/localization/FooBundle_en.java
Index: FooBundle_en.java
===================================================================
package org.apache.fulcrum.localization;
import java.util.ListResourceBundle;
/**
* An english resource bundle for use in testing.
*/
public class FooBundle_en extends ListResourceBundle
{
private static final Object[][] CONTENTS =
{
{ "key1", "value1" },
{ "key2", "value2" },
{ "key3", "value3" },
{ "key4", "value4" }
};
protected Object[][] getContents()
{
return CONTENTS;
}
}
1.1 jakarta-turbine-fulcrum/localization/src/test/org/apache/fulcrum/localization/BarBundle_ko_KR.java
Index: BarBundle_ko_KR.java
===================================================================
package org.apache.fulcrum.localization;
import java.util.ListResourceBundle;
/**
* A dummied up Korean resource bundle for use in testing.
*/
public class BarBundle_ko_KR extends ListResourceBundle
{
private static final Object[][] CONTENTS =
{
{ "key1", "[ko] value1" },
{ "key2", "[ko] value2" },
{ "key3", "[ko] value3" },
};
protected Object[][] getContents()
{
return CONTENTS;
}
}
1.1 jakarta-turbine-fulcrum/localization/src/test/org/apache/fulcrum/localization/LocalizationTest.java
Index: LocalizationTest.java
===================================================================
package org.apache.fulcrum.localization;
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001-2002 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache Turbine" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache Turbine", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
import java.util.Locale;
import java.util.MissingResourceException;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.fulcrum.testcontainer.BaseUnitTest;
/**
* Tests the API of the
* {@link org.apache.fulcrum.localization.LocalizationService}.
* <br>
*
* @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
* @author <a href="mailto:jason@zenplex.com">Jason van Zyl</a>
*/
public class LocalizationTest extends BaseUnitTest
{
private LocalizationService ls = null;
public LocalizationTest(String name)
{
super(name);
}
public static Test suite()
{
return new TestSuite(LocalizationTest.class);
}
public void setUp()
{
super.setUp();
try
{
ls = (LocalizationService) this.lookup(LocalizationService.ROLE);
}
catch (ComponentException e)
{
e.printStackTrace();
fail(e.getMessage());
}
// this.release(sc);
}
public void testInitialization()
{
assertTrue(true);
}
public void testLocalization() throws Exception
{
// Test retrieval of text using multiple default bundles
String s = ls.getString(null, null, "key1");
assertEquals("Unable to retrieve localized text for locale: default", "value1", s);
s = ls.getString(null, new Locale("en", "US"), "key2");
assertEquals("Unable to retrieve localized text for locale: en-US", "value2", s);
s = ls.getString("org.apache.fulcrum.localization.BarBundle", new Locale("ko", "KR"), "key3");
assertEquals("Unable to retrieve localized text for locale: ko-KR", s, "[ko] value3");
try
{
ls.getString("DoesNotExist", new Locale("ko", ""), "key1");
fail();
}
catch (MissingResourceException expectedFailure)
{
// Asked for resource bundle which does not exist.
}
// When a locale is used which cannot be produced for a given
// bundle, fall back to the default locale.
s = ls.getString(null, new Locale("ko", "KR"), "key4");
assertEquals("Unable to retrieve localized text for locale: default", s, "value4");
try
{
ls.getString(null, null, "NoSuchKey");
fail();
}
catch (MissingResourceException expectedFailure)
{
// Asked for key from default bundle which does not exist,
}
}
/**
* Putting this in a seperate testcase because it fails.. Why? I don't know. I have never
* used localization, so I leave it to brains better then mine. -dep
* @throws Exception
*/
public void testRetrievingOddLocale() throws Exception
{
String s = ls.getString(null, new Locale("fr", "US"), "key3");
assertEquals("Unable to retrieve localized text for locale: fr", "[fr] value3", s);
}
}
1.1 jakarta-turbine-fulcrum/localization/src/test/org/apache/fulcrum/localization/FooBundle_fr.java
Index: FooBundle_fr.java
===================================================================
package org.apache.fulcrum.localization;
import java.util.ListResourceBundle;
/**
* A dummied up French resource bundle for use in testing.
*/
public class FooBundle_fr extends ListResourceBundle
{
private static final Object[][] CONTENTS =
{
{ "key1", "[fr] value1" },
{ "key2", "[fr] value2" },
{ "key3", "[fr] value3" },
{ "key4", "[fr] value4" }
};
protected Object[][] getContents()
{
return CONTENTS;
}
}
1.1 jakarta-turbine-fulcrum/localization/src/test/TestComponentConfig.xml
Index: TestComponentConfig.xml
===================================================================
<componentConfig>
<localization>
<bundles>
<bundle>org.apache.fulcrum.localization.BarBundle</bundle>
<bundle>org.apache.fulcrum.localization.FooBundle</bundle>
<bundle>org.apache.fulcrum.localization.MissingBundle</bundle>
</bundles>
</localization>
</componentConfig>
1.1 jakarta-turbine-fulcrum/localization/src/test/TestRoleConfig.xml
Index: TestRoleConfig.xml
===================================================================
<!-- This configuration file for Avalon components is used for testing the TestComponent -->
<role-list>
<role
name="org.apache.fulcrum.localization.LocalizationService"
shorthand="localization"
default-class="org.apache.fulcrum.localization.DefaultLocalizationService"/>
</role-list>