All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jakarta.servlet.jsp.jstl.fmt.LocaleSupport Maven / Gradle / Ivy

There is a newer version: 3.0.2
Show newest version
/*
 * Copyright (c) 1997-2020 Oracle and/or its affiliates. All rights reserved.
 * Copyright 2004 The Apache Software Foundation
 * Copyright (c) 2021-2021 Contributors to the Eclipse Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package jakarta.servlet.jsp.jstl.fmt;

import java.text.MessageFormat;
import java.util.Enumeration;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.jsp.PageContext;
import jakarta.servlet.jsp.jstl.core.Config;

/**
 * Class which exposes the locale-determination logic for resource bundles through convenience methods.
 *
 * 

* This class may be useful to any tag handler implementation that needs to produce localized messages. For example, * this might be useful for exception messages that are intended directly for user consumption on an error page. * * @author Jan Luehe */ public class LocaleSupport { private static final String UNDEFINED_KEY = "???"; private static final char HYPHEN = '-'; private static final char UNDERSCORE = '_'; private static final String REQUEST_CHAR_SET = "jakarta.servlet.jsp.jstl.fmt.request.charset"; private static final Locale EMPTY_LOCALE = new Locale("", ""); /** * Retrieves the localized message corresponding to the given key. * *

* The given key is looked up in the resource bundle of the default I18N localization context, which is retrieved from * the jakarta.servlet.jsp.jstl.fmt.localizationContext configuration setting. * *

* If the configuration setting is empty, or the default I18N localization context does not contain any resource bundle, * or the given key is undefined in its resource bundle, the string "???<key>???" is returned, where "<key>" * is replaced with the given key. * * @param pageContext the page in which to get the localized message corresponding to the given key * @param key the message key * * @return the localized message corresponding to the given key */ public static String getLocalizedMessage(PageContext pageContext, String key) { return getLocalizedMessage(pageContext, key, null, null); } /** * Retrieves the localized message corresponding to the given key. * *

* The given key is looked up in the resource bundle with the given base name. * *

* If no resource bundle with the given base name exists, or the given key is undefined in the resource bundle, the * string "???<key>???" is returned, where "<key>" is replaced with the given key. * * @param pageContext the page in which to get the localized message corresponding to the given key * @param key the message key * @param basename the resource bundle base name * * @return the localized message corresponding to the given key */ public static String getLocalizedMessage(PageContext pageContext, String key, String basename) { return getLocalizedMessage(pageContext, key, null, basename); } /** * Retrieves the localized message corresponding to the given key, and performs parametric replacement using the * arguments specified via args. * *

* See the specification of the <fmt:message> action for a description of how parametric replacement is * implemented. * *

* The localized message is retrieved as in * {@link #getLocalizedMessage(jakarta.servlet.jsp.PageContext,java.lang.String) getLocalizedMessage(pageContext, key)}. * * @param pageContext the page in which to get the localized message corresponding to the given key * @param key the message key * @param args the arguments for parametric replacement * * @return the localized message corresponding to the given key */ public static String getLocalizedMessage(PageContext pageContext, String key, Object[] args) { return getLocalizedMessage(pageContext, key, args, null); } /** * Retrieves the localized message corresponding to the given key, and performs parametric replacement using the * arguments specified via args. * *

* See the specification of the <fmt:message> action for a description of how parametric replacement is * implemented. * *

* The localized message is retrieved as in * {@link #getLocalizedMessage(jakarta.servlet.jsp.PageContext,java.lang.String, java.lang.String) * getLocalizedMessage(pageContext, key, basename)}. * * @param pageContext the page in which to get the localized message corresponding to the given key * @param key the message key * @param args the arguments for parametric replacement * @param basename the resource bundle base name * * @return the localized message corresponding to the given key */ public static String getLocalizedMessage(PageContext pageContext, String key, Object[] args, String basename) { LocalizationContext locCtxt = null; String message = UNDEFINED_KEY + key + UNDEFINED_KEY; if (basename != null) { locCtxt = getLocalizationContext(pageContext, basename); } else { locCtxt = getLocalizationContext(pageContext); } if (locCtxt != null) { ResourceBundle bundle = locCtxt.getResourceBundle(); if (bundle != null) { try { message = bundle.getString(key); if (args != null) { MessageFormat formatter = new MessageFormat(""); if (locCtxt.getLocale() != null) { formatter.setLocale(locCtxt.getLocale()); } formatter.applyPattern(message); message = formatter.format(args); } } catch (MissingResourceException mre) { } } } return message; } /** * Gets the default I18N localization context. * * @param pc Page in which to look up the default I18N localization context */ private static LocalizationContext getLocalizationContext(PageContext pc) { LocalizationContext locCtxt = null; Object obj = Config.find(pc, Config.FMT_LOCALIZATION_CONTEXT); if (obj == null) { return null; } if (obj instanceof LocalizationContext) { locCtxt = (LocalizationContext) obj; } else { // localization context is a bundle basename locCtxt = getLocalizationContext(pc, (String) obj); } return locCtxt; } /** * Gets the resource bundle with the given base name, whose locale is determined as follows: * * Check if a match exists between the ordered set of preferred locales and the available locales, for the given base * name. The set of preferred locales consists of a single locale (if the jakarta.servlet.jsp.jstl.fmt.locale * configuration setting is present) or is equal to the client's preferred locales determined from the client's browser * settings. * *

* If no match was found in the previous step, check if a match exists between the fallback locale (given by the * jakarta.servlet.jsp.jstl.fmt.fallbackLocale configuration setting) and the available locales, for the given * base name. * * @param pageContext Page in which the resource bundle with the given base name is requested * @param basename Resource bundle base name * * @return Localization context containing the resource bundle with the given base name and the locale that led to the * resource bundle match, or the empty localization context if no resource bundle match was found */ private static LocalizationContext getLocalizationContext(PageContext pc, String basename) { LocalizationContext locCtxt = null; ResourceBundle bundle = null; if (basename == null || basename.equals("")) { return new LocalizationContext(); } // Try preferred locales Locale pref = getLocale(pc, Config.FMT_LOCALE); if (pref != null) { // Preferred locale is application-based bundle = findMatch(basename, pref); if (bundle != null) { locCtxt = new LocalizationContext(bundle, pref); } } else { // Preferred locales are browser-based locCtxt = findMatch(pc, basename); } if (locCtxt == null) { // No match found with preferred locales, try using fallback locale pref = getLocale(pc, Config.FMT_FALLBACK_LOCALE); if (pref != null) { bundle = findMatch(basename, pref); if (bundle != null) { locCtxt = new LocalizationContext(bundle, pref); } } } if (locCtxt == null) { // try using the root resource bundle with the given basename try { bundle = ResourceBundle.getBundle(basename, EMPTY_LOCALE, Thread.currentThread().getContextClassLoader()); if (bundle != null) { locCtxt = new LocalizationContext(bundle, null); } } catch (MissingResourceException mre) { // do nothing } } if (locCtxt != null) { // set response locale if (locCtxt.getLocale() != null) { setResponseLocale(pc, locCtxt.getLocale()); } } else { // create empty localization context locCtxt = new LocalizationContext(); } return locCtxt; } /* * Determines the client's preferred locales from the request, and compares each of the locales (in order of preference) * against the available locales in order to determine the best matching locale. * * @param pageContext the page in which the resource bundle with the given base name is requested * * @param basename the resource bundle's base name * * @return the localization context containing the resource bundle with the given base name and best matching locale, or * null if no resource bundle match was found */ private static LocalizationContext findMatch(PageContext pageContext, String basename) { LocalizationContext locCtxt = null; // Determine locale from client's browser settings. for (Enumeration enum_ = getRequestLocales((HttpServletRequest) pageContext.getRequest()); enum_.hasMoreElements();) { Locale pref = (Locale) enum_.nextElement(); ResourceBundle match = findMatch(basename, pref); if (match != null) { locCtxt = new LocalizationContext(match, pref); break; } } return locCtxt; } /* * Gets the resource bundle with the given base name and preferred locale. * * This method calls java.util.ResourceBundle.getBundle(), but ignores its return value unless its locale represents an * exact or language match with the given preferred locale. * * @param basename the resource bundle base name * * @param pref the preferred locale * * @return the requested resource bundle, or null if no resource bundle with the given base name exists or if * there is no exact- or language-match between the preferred locale and the locale of the bundle returned by * java.util.ResourceBundle.getBundle(). */ private static ResourceBundle findMatch(String basename, Locale pref) { ResourceBundle match = null; try { ResourceBundle bundle = ResourceBundle.getBundle(basename, pref, Thread.currentThread().getContextClassLoader()); Locale avail = bundle.getLocale(); if (pref.equals(avail) || (pref.getLanguage().equals(avail.getLanguage()) && ("".equals(avail.getCountry()) || pref.getCountry().equals(avail.getCountry())))) { // Exact match match = bundle; } } catch (MissingResourceException mre) { } return match; } /* * Returns the locale specified by the named scoped attribute or context configuration parameter. * *

The named scoped attribute is searched in the page, request, session (if valid), and application scope(s) (in * this order). If no such attribute exists in any of the scopes, the locale is taken from the named context * configuration parameter. * * @param pageContext the page in which to search for the named scoped attribute or context configuration parameter * * @param name the name of the scoped attribute or context configuration parameter * * @return the locale specified by the named scoped attribute or context configuration parameter, or null if no * scoped attribute or configuration parameter with the given name exists */ private static Locale getLocale(PageContext pageContext, String name) { Locale loc = null; Object obj = Config.find(pageContext, name); if (obj != null) { if (obj instanceof Locale) { loc = (Locale) obj; } else { loc = parseLocale((String) obj); } } return loc; } /* * Stores the given locale in the response object of the given page context, and stores the locale's associated charset * in the jakarta.servlet.jsp.jstl.fmt.request.charset session attribute, which may be used by the * action in a page invoked by a form included in the response to set the request charset to the same as the response * charset (this makes it possible for the container to decode the form parameter values properly, since browsers * typically encode form field values using the response's charset). * * @param pageContext the page context whose response object is assigned the given locale * * @param locale the response locale */ private static void setResponseLocale(PageContext pc, Locale locale) { // set response locale ServletResponse response = pc.getResponse(); response.setLocale(locale); // get response character encoding and store it in session attribute if (pc.getSession() != null) { try { pc.setAttribute(REQUEST_CHAR_SET, response.getCharacterEncoding(), PageContext.SESSION_SCOPE); } catch (IllegalStateException ex) { } // invalidated session ignored } } /** * See parseLocale(String, String) for details. */ private static Locale parseLocale(String locale) { return parseLocale(locale, null); } /** * Parses the given locale string into its language and (optionally) country components, and returns the corresponding * java.util.Locale object. * * If the given locale string is null or empty, the runtime's default locale is returned. * * @param locale the locale string to parse * @param variant the variant * * @return java.util.Locale object corresponding to the given locale string, or the runtime's default locale if * the locale string is null or empty * * @throws IllegalArgumentException if the given locale does not have a language component or has an empty country * component */ private static Locale parseLocale(String locale, String variant) { Locale ret = null; String language = locale; String country = null; int index = -1; if ((index = locale.indexOf(HYPHEN)) > -1 || (index = locale.indexOf(UNDERSCORE)) > -1) { language = locale.substring(0, index); country = locale.substring(index + 1); } if (language == null || language.length() == 0) { throw new IllegalArgumentException("Missing language component in 'value' attribute in setLocale"); } if (country == null) { if (variant != null) { ret = new Locale(language, "", variant); } else { ret = new Locale(language, ""); } } else if (country.length() > 0) { if (variant != null) { ret = new Locale(language, country, variant); } else { ret = new Locale(language, country); } } else { throw new IllegalArgumentException("Empty country component in 'value' attribute in setLocale"); } return ret; } /** * HttpServletRequest.getLocales() returns the server's default locale if the request did not specify a preferred * language. We do not want this behavior, because it prevents us from using the fallback locale. We therefore need to * return an empty Enumeration if no preferred locale has been specified. This way, the logic for the fallback locale * will be able to kick in. */ private static Enumeration getRequestLocales(HttpServletRequest request) { Enumeration values = request.getHeaders("accept-language"); if (values.hasMoreElements()) { // At least one "accept-language". Simply return // the enumeration returned by request.getLocales(). // System.out.println("At least one accept-language"); return request.getLocales(); } else { // No header for "accept-language". Simply return // the empty enumeration. // System.out.println("No accept-language"); return values; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy