com.oreilly.servlet.LocaleNegotiator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cos Show documentation
Show all versions of cos Show documentation
Cos.
The version 2024.5.0.0 is for Servlet 5.0.0.
// Copyright (C) 1998-2001 by Jason Hunter .
// All rights reserved. Use of this class is limited.
// Please see the LICENSE for more information.
package com.oreilly.servlet;
import java.io.*;
import java.util.*;
import com.oreilly.servlet.LocaleToCharsetMap;
/**
* A class to aid in servlet internationalization. It determines, from a
* client request, the best charset, locale, and resource bundle to use
* with the response.
*
* LocaleNegotiator works by scanning through the client's language
* preferences (sent by browsers in the Accept-Language header)
* looking for any
* language for which there exists is a corresponding resource bundle.
* When it finds a correspondence, it uses the LocaleToCharsetMap class
* to determine the charset. If there's any problem, it tries to fall
* back to US English. The logic currently ignores the client's charset
* preferences (sent in the Accept-Charset header).
*
* It can be used like this:
*
* String bundleName = "BundleName";
* String acceptLanguage = req.getHeader("Accept-Language");
* String acceptCharset = req.getHeader("Accept-Charset");
*
* LocaleNegotiator negotiator =
* new LocaleNegotiator(bundleName, acceptLanguage, acceptCharset);
*
* Locale locale = negotiator.getLocale();
* String charset = negotiator.getCharset();
* ResourceBundle bundle = negotiator.getBundle(); // may be null
*
* res.setContentType("text/plain; charset=" + charset);
* res.setHeader("Content-Language", locale.getLanguage());
* res.setHeader("Vary", "Accept-Language");
*
* PrintWriter out = res.getWriter();
*
* out.println(bundle.getString("resource"));
*
*
* @see com.oreilly.servlet.LocaleToCharsetMap
*
* @author Jason Hunter, Copyright © 1998
* @version 1.0, 98/09/18
*/
public class LocaleNegotiator {
private ResourceBundle chosenBundle;
private Locale chosenLocale;
private String chosenCharset;
/**
* Constructs a new LocaleNegotiator for the given bundle name, language
* list, and charset list.
*
* @param bundleName the resource bundle name
* @param languages the Accept-Language header
* @param charsets the Accept-Charset header
*/
public LocaleNegotiator(String bundleName,
String languages,
String charsets) {
// Specify default values:
// English language, ISO-8859-1 (Latin-1) charset, English bundle
Locale defaultLocale = new Locale("en", "US");
String defaultCharset = "ISO-8859-1";
ResourceBundle defaultBundle = null;
try {
defaultBundle = ResourceBundle.getBundle(bundleName, defaultLocale);
}
catch (MissingResourceException e) {
// No default bundle was found. Flying without a net.
}
// If the client didn't specify acceptable languages, we can keep
// the defaults.
if (languages == null) {
chosenLocale = defaultLocale;
chosenCharset = defaultCharset;
chosenBundle = defaultBundle;
return; // quick exit
}
// Use a tokenizer to separate acceptable languages
StringTokenizer tokenizer = new StringTokenizer(languages, ",");
while (tokenizer.hasMoreTokens()) {
// Get the next acceptable language.
// (The language can look something like "en; qvalue=0.91")
String lang = tokenizer.nextToken();
// Get the locale for that language
Locale loc = getLocaleForLanguage(lang);
// Get the bundle for this locale. Don't let the search fallback
// to match other languages!
ResourceBundle bundle = getBundleNoFallback(bundleName, loc);
// The returned bundle is null if there's no match. In that case
// we can't use this language since the servlet can't speak it.
if (bundle == null) continue; // on to the next language
// Find a charset we can use to display that locale's language.
String charset = getCharsetForLocale(loc, charsets);
// The returned charset is null if there's no match. In that case
// we can't use this language since the servlet can't encode it.
if (charset == null) continue; // on to the next language
// If we get here, there are no problems with this language.
chosenLocale = loc;
chosenBundle = bundle;
chosenCharset = charset;
return; // we're done
}
// No matches, so we let the defaults stand
chosenLocale = defaultLocale;
chosenCharset = defaultCharset;
chosenBundle = defaultBundle;
}
/**
* Gets the chosen bundle.
*
* @return the chosen bundle
*/
public ResourceBundle getBundle() {
return chosenBundle;
}
/**
* Gets the chosen locale.
*
* @return the chosen locale
*/
public Locale getLocale() {
return chosenLocale;
}
/**
* Gets the chosen charset.
*
* @return the chosen charset
*/
public String getCharset() {
return chosenCharset;
}
/*
* Gets a Locale object for a given language string
*/
private Locale getLocaleForLanguage(String lang) {
Locale loc;
int semi, dash;
// Cut off any qvalue that might come after a semi-colon
if ((semi = lang.indexOf(';')) != -1) {
lang = lang.substring(0, semi);
}
// Trim any whitespace
lang = lang.trim();
// Create a Locale from the language. A dash may separate the
// language from the country.
if ((dash = lang.indexOf('-')) == -1) {
loc = new Locale(lang, ""); // No dash, no country
}
else {
loc = new Locale(lang.substring(0, dash), lang.substring(dash+1));
}
return loc;
}
/*
* Gets a ResourceBundle object for the given bundle name and locale,
* or null if the bundle can't be found. The resource bundle must match
* the locale exactly. Fallback matches are not permitted.
*/
private ResourceBundle getBundleNoFallback(String bundleName, Locale loc) {
// First get the fallback bundle -- the bundle that will be selected
// if getBundle() can't find a direct match. This bundle can be
// compared to the bundles returned by later calls to getBundle() in
// order to detect when getBundle() finds a direct match.
ResourceBundle fallback = null;
try {
fallback =
ResourceBundle.getBundle(bundleName, new Locale("bogus", ""));
}
catch (MissingResourceException e) {
// No fallback bundle was found.
}
try {
// Get the bundle for the specified locale
ResourceBundle bundle = ResourceBundle.getBundle(bundleName, loc);
// Is the bundle different than our fallback bundle?
if (bundle != fallback) {
// We have a real match!
return bundle;
}
// So the bundle is the same as our fallback bundle.
// We can still have a match, but only if our locale's language
// matches the default locale's language.
else if (bundle == fallback &&
loc.getLanguage().equals(Locale.getDefault().getLanguage())) {
// Another way to match
return bundle;
}
else {
// No match, keep looking
}
}
catch (MissingResourceException e) {
// No bundle available for this locale
}
return null; // no match
}
/**
* Gets the best charset for a given locale, selecting from a charset list.
* Currently ignores the charset list. Subclasses can override this
* method to take the list into account.
*
* @param loc the locale
* @param charsets a comma-separated charset list
* @return the best charset for the given locale from the given list
*/
protected String getCharsetForLocale(Locale loc, String charsets) {
// Note: This method ignores the client-specified charsets
return LocaleToCharsetMap.getCharset(loc);
}
}