Ajax Hacks: Tips & Tools for Creating Responsive Web Sites

Hack 41. Find the Browser's Locale Information

Use XMLHttpRequest to find out more specific locale information about a user.

When a user requests a web page, the browser typically sends along some extra data as part of a request header that indicates the user's preferred language. This information is the value of the Accept-Language request headerfor example, en_us for the English language as spoken in the United States, or ko_kr for Korean as spoken in South Korea.

In JavaScript, you can use the navigator.language (or, for Internet Explorer, navigator.userLanguage) property value to pick up this internationalization data. This hack grabs this information for display to the user, then gives the user the option of displaying a more specific translation of the [language code]_[country code] term, as in English_United States.

This hack uses the following sources: http://www.unicode.org/unicode/onlinedat/languages.html for the language codes, and http://www.iso.org/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/index.html for country codes.

Figure 4-23 shows the hack in the Safari browser.

Figure 4-23. An English language preference

When a user loads the page into the browser, it displays the value of that user's language preference in the form [two-letter language code] or [two-letter language code]_[two-letter country code]. The user then clicks the "Define preference" button, and a translation of the code(s) appears, without a page refresh. XMLHttpRequest provides a country and/or language code to a server component, which checks the sources referenced in the previous note and returns a translation of the code or codes (e.g., Korean instead of ko).

I found that changing my language preferences in the browser (from, say, en_us to es_es) did not cause the value of navigator.language or navigator.userLanguage to change. This property value appears to be a rather static value associated with the browser. To get around this, applications can use a server component that reads the Accept-Language request header directly. Accept-Language typically contains a list of any language codes that the user has set in the browser.

Here is a subset of the web page code for this hack:

<head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <script type="text/javascript" src="/books/4/254/1/html/2/js/hacks4_13.js"></script> <script type="text/javascript" src="/books/4/254/1/html/2/js/http_request.js"></script> <link rel="stylesheet" type="text/css" href="/css/hacks.css" /> <title>Where do you come from?</title> </head> <body> <h3>Welcome</h3> <p> Your language preferences have been identified as: <span ></span> </p> <form action="javascript:void%200"> <p> <button type="button">Define preference</button> </p> </form> <p> <span ></span> </p>

hacks4_13.js contains the JavaScript for this hack. The request object is powered by http_request.js (see "Use Your Own Library for XMLHttpRequest" [Hack #3]).

The Interpreter

Here is the code in hacks4_13.js. It reads the navigator.language/navigator.userLanguage value and, after parsing the value, sends it to a server component:

var lgn=""; window.onload=function( ){ if(navigator.language) { lgn=navigator.language; } else if(navigator.userLanguage) { lgn=navigator.userLanguage; } if(lgn.length >= 2){ displayLanguage(lgn); } else { showMsg(document.getElementById("msg"), "Sorry, no language information is available "+ "from your browser."); } var b1 = document.getElementById("b1"); if(b1) { //Extract the language and country codes //The value may be a language code only //as in "es" for Spanish b1.onclick=function( ){ var lg = lgn.substring(0,2); var ct = lgn.length > 2 ? lgn.substring(3) : ""; var _url="http://www.parkerriver.com/s/lang?lang="+ lg+"&country="+ct; httpRequest("GET",_url,true,handleResponse); } } } function showMsg(_id,txt){ if(_id && txt){_id.innerHTML=txt;} } function clearMsg(_id){ if(_id){_id.innerHTML="";} } function displayLanguage(_code){ showMsg(document.getElementById("_country"), "<strong>"+_code+"</strong>"); } function handleResponse( ){ try{ if(request.readyState == 4){ if(request.status == 200){ var resp = request.responseXML; //Pull out the content of the country //and language elements and display them //to the user if(resp != null){ var intl=resp.getElementsByTagName("intl")[0]; var c= intl.getElementsByTagName("country")[0]; var l= intl.getElementsByTagName("language")[0]; var lval=""; var cval=""; if(l.hasChildNodes( )){lval=l.childNodes[0].nodeValue;} if(c.hasChildNodes( )){cval=c.childNodes[0].nodeValue;} if(lval && cval) { showMsg(document.getElementById("msg"), "<strong>"+lval+ "_"+cval+"</strong>"); } else if (lval && ! cval) { showMsg(document.getElementById("msg"), "<strong>"+lval+"</strong>"); } else if (! lval && cval){ showMsg(document.getElementById("msg"), "<strong>"+cval+"</strong>"); } } else { showMsg(document. getElementById("msg"), "The language info could not be accessed "+ "at this time."); } } else { //snipped...See Hack #3 }

When the application uses the request object to connect with the server component, the component sends back some XML. Here is a sample XML return value:

<intl> <country>KOREA, REPUBLIC OF</country> <language>Korean</language> </intl>

The handleResponse( ) function acquires the XML with var resp = request.responseXML. The function then parses the XML, displaying these values to the user.

Hacking the Hack

As mentioned earlier, a most likely improved iteration of this hack would use a server component to read the Accept-Language header directly, rather than depend on the navigator.language property in JavaScript. For example, you can use a JavaServer Pages (JSP) file that reads the Accept-Language header, then uses the embedded Ajax code to display the translation as done here.

Категории