Automatically selecting country and language for user in Java Servlet

newbie picture newbie · May 13, 2011 · Viewed 15.2k times · Source

I have to detect user country and language automatically in Java Servlet using request details (IP address, browser information etc.). Is it possible to detect these settings for the most of users (~90%)?

Answer

Paweł Dyda picture Paweł Dyda · May 14, 2011

Detecting the language

Detecting the correct language is easy. Web browsers tend to send AcceptLanguage header and Java Servlet API is so nice to actually convert it contents to Locale object(s). All you would have to do, is just access this information and implement fall-back mechanism. To do that you actually need a list of Locales your application is going to support (you could think of creating some sort of Properties file that would contain supported locales along with default one). Example below shows such implementation:

public class LousyServlet extends HttpServlet {
    private Properties supportedLanguages;
    private Locale requestLocale = (Locale) supportedLanguages.get("DEFAULT");

    public LousyServlet() {
        supportedLanguages = new Properties();
        // Just for demonstration of the concept
        // you would probably load it from i.e. XML
        supportedLanguages.put("DEFAULT", Locale.US);
        // example mapping of "de" to "de_DE"
        supportedLanguages.put("de-DEFAULT", Locale.GERMANY);
        supportedLanguages.put("de_AT", new Locale("de", "AT"));
        supportedLanguages.put("de_CH", new Locale("de", "CH"));
        supportedLanguages.put("ja_JP", Locale.JAPAN);
    }

    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        detectLocale(request);

        super.doGet(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        detectLocale(request);

        super.doPost(request, response);
    }

    private void detectLocale(HttpServletRequest request) {
        Enumeration locales = request.getLocales();
        while (locales.hasMoreElements()) {
            Locale locale = (Locale) locales.nextElement();
            if (supportedLanguages.contains(locale)) {
                requestLocale = locale;
                break;
            }
        }
    }

    public String getLanguage() {
        // get English name of the language
        // For native call requestLocale.getDisplayName(requestLocale)
        return requestLocale.getDisplayLanguage();
    }
}

Mind you, that you would need to list all the countries for given language, as it won't fall back in this case. That is for the reason. Local users tend to have non-specific Locale either way (for example my web browser sends pl_PL, pl, en_US, en in that order). And the reason is, there are some languages that differs substantially depending on the country, for example Brazilian Portuguese is different than Portuguese and Chinese Traditional (Taiwan, Hong Kong) is different than Chinese Simplified (China, Singapore) and it won't be appropriate to fall back to one of them.

Detecting country

Depending on what you need this information for, it might or might not be straightforward. If end user's web browser is configured correctly, it will give you the hint of end user's preferred location - that would be the part of Locale. If you only need that information to decide on which localized page to load, that would be probably the best option. Of course if Locale object is not specific (country-less) you may want to assign "default" country for each supported non-specific Locale. In both cases, you should provide end user with some means of switching country (i.e. through "Other countries" combo box). The list could be obtained like this:

public String[] getOtherCountries() {
    Set<String> countries = new HashSet<String>();
    Set<Object> keys = supportedLanguages.keySet();
    for (Object key : keys) {
        Locale other = (Locale) supportedLanguages.get(key);
        if (other != requestLocale) {
            countries.add(other.getDisplayCountry(requestLocale));
        }
    }

    return countries.toArray(new String[0]);
}

If however, you need this to restrict the access to the contents based on location, the problem is harder. You may think of checking the IP. You would need to prepare some Database with address classes that belongs to given country. This data could be found on the Internet. The only problem with that solution is, user may configure a web proxy and fool your web site on his real location. Also, corporate users might appear as if they connect from USA where in fact they connect from UK or Ireland. Either way, it is your best shot.

There was some question on GeoLocation before and I believe you may find it useful. Here you are.