Brian Kessler
Brian Kessler

Reputation: 2327

Why isn't my JSP displaying in the German (de_DE) locale when I use <fmt:setLocale>?

I've created the following JSP:

<!-- WebContent/pages/ResourceBundlesJST.jsp -->
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<%@ page import="java.text.*" %>
<%@ page import="java.util.*" %>
<%@ page import="hu.flux.locale.LanguageToolkit" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%  
    Locale locale = LanguageToolkit.getLanguage(request);
    //String locale = LanguageToolkit.getLanguageString(request);
%>
<fmt:setLocale value="${locale}" />
<fmt:bundle basename="hu.flux.locale.resources.TestResources">
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
    <title>Insert title here</title>
    </head>
    <body>
        <h1><fmt:message key="greetHeading"/></h1>
        <p><fmt:message key="welcomeText"/></p>
        <p>Your locale is <%= locale %>.</p>
        <form action="your_form_handler_here" method="post">
            <div>
                <label for="name"><fmt:message key="namePrompt"/></label>
                <input type="text" id="name" name="name">
            </div>
            <div>
                <label for="age"><fmt:message key="agePrompt"/></label>
                <input type="text" id="age" name="age">
            </div>
            <div>
                <label for="place"><fmt:message key="placePrompt"/></label>
                <input type="text" id="place" name="place">
            </div>
            <input type="submit" value="<fmt:message key="submitButtonText"/>">
        </form>
    </body>
    </html>
</fmt:bundle>

When I try to visit the page with this URL:

http://localhost:8080/SamsTeachYourselfJSP/pages/ResourceBundlesJSTL.jsp?languageOverride=de_DE

This is displayed to the screen:

Hello!

Welcome to our web site. Please take a moment to fill out our survey

Your locale is de_DE.

What is your name:  
How old are you:  
Where do you live:  

The page is evidently finding and using the English properties file instead of the German one even though the server picked up my parameter to set the locale to de_DE and accepted the command to set the locale.

The resource I expect it to call contains:

# /src/hu/flux/locale/resources/TestResources_de.properties
namePrompt=Wie hei[gb]en Sie:
agePrompt=Wie alt sind Sie:
placePrompt=Wo wohnen Sie:
greetHeading=Guten Tag!
welcomeText= Willkommen bei unserer Web-Site.  Bitte, dauern Sie einen Moment Um unsere Umfrage auszufüllen
submitButtonText=Senden

I'm pretty sure the problem isn't in my LanguageToolkit class since that works fine with a non-JSTL version of this page, but if anyone wants to see it:

/**
 * /src/hu/flux/locale/LanguageToolkit.java
 */
package hu.flux.locale;

import java.util.Locale;
import java.util.StringTokenizer;

import javax.servlet.http.HttpServletRequest;

/**
 * @author Brian Kessler
 *
 */
public class LanguageToolkit {

    /**
     * 
     */
    public LanguageToolkit() {
        // TODO Auto-generated constructor stub
    }

    public static Locale getLanguage(HttpServletRequest request)
    {
        Locale locale = Locale.getDefault();

        // Get the browser's preferred language.
        String acceptLangString = request.getHeader("ACCEPT-LANGAUGE");

        // Allow the user to override the browser's langauge setting.
        // This lets you test with tools such as Babelfish 
        // (which isn't that great at translating to begin with).
        String override = request.getParameter ("languageOverride");
        if (override != null) { acceptLangString = override; }

        // If there is an ACCEPT-LANGUAGE header, parse it.
        if (acceptLangString != null) 
        {  
            Locale acceptedLocale = parseLangString (acceptLangString);
            if (acceptedLocale != null) {locale = acceptedLocale;}
        }

        return locale;
    }

    public static String getLanguageString(HttpServletRequest request)
    {
        String locale = "EN-uk";

        // Get the browser's preferred language.
        String acceptLangString = request.getHeader("ACCEPT-LANGAUGE");

        // Allow the user to override the browser's langauge setting.
        // This lets you test with tools such as Babelfish 
        // (which isn't that great at translating to begin with).
        String override = request.getParameter ("languageOverride");
        if (override != null) { acceptLangString = override; }

        // If there is an ACCEPT-LANGUAGE header, parse it.
        if (acceptLangString != null)  {locale = acceptLangString;}

        return locale;
    }

    private static Locale parseLangString(String acceptLangString) 
    {
        // The accepted languages should be separated by commas, but also
        // add space as a separator to eliminate whitespace.
        StringTokenizer localeParser = new StringTokenizer(acceptLangString, " ,");

        // See whether there is a language in the list (you need only the first one).
        if (localeParser.hasMoreTokens())
        {
            // Get the locale.
            String localeStr = localeParser.nextToken();

            // The local should be in the format ll-CC where 11 is the language
            // and CC is the country, like en-US for English in the U.S. and
            // de-DE for German in Germany.  Allow the browser to use _ instead
            // of -, too.
            StringTokenizer localeSplitter = new StringTokenizer (localeStr, "_-");

            // Assume both values are blank.
            String language = "";
            String country = "";

            // See whether a language is specified.
            if (localeSplitter.hasMoreTokens()) {language = localeSplitter.nextToken(); }

            // See whether a country is specified (there won't always be one).
            if (localeSplitter.hasMoreTokens()) {country = localeSplitter.nextToken(); }

            // Create a local based on this language and country (if country is blank,
            // you'll still get locale-based text, but currencies won't display correctly.
            return (new Locale(language, country));
        }
        return null;
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

    }

}

Any ideas why I am seeing English and how to fix it?

Upvotes: 1

Views: 5322

Answers (1)

BalusC
BalusC

Reputation: 1109302

There are 2 problems:

First, the fmt:setLocale TLDDOC says the following:

value - java.lang.String - A String value is interpreted as the printable representation of a locale, which must contain a two-letter (lower-case) language code (as defined by ISO-639), and may contain a two-letter (upper-case) country code (as defined by ISO-3166). Language and country codes must be separated by hyphen (-) or underscore (_).

In other words, you can't set it with a java.util.Locale.

Second, anything which is been declared using scriptlets cannot be accessed in EL. EL can only access attributes which are been placed in PageContext, HttpServletRequest, HttpSession or ServletContext by its setAttribute() method. Under the covers, EL basically does a pageContext.findAttribute(name) for a ${name}. There are basically 4 solutions in order from least to best recommendation:

  1. Use scriptlet instead of EL in <fmt:setLocale>.
  2. Put the locale in request scope by request.setAttribute("locale", locale); inside scriptlet.
  3. Get rid of scriptlets and declare LanguageToolkit as an EL function.
  4. Create a Filter which does the job.

That said, I would recommend to use <fmt:setBundle> instead of <fmt:bundle> in this particular case since you seem want to cover the entire page. I would also recommend to use HttpServletRequest#getLocale() instead of manually parsing the request header. The correct algorithm is more complex than what you've as far.

Upvotes: 1

Related Questions