New things with jQuery, Part 1: Globalize

Make your web app a global traveler

The new Globalize JavaScript plugin allows anyone to make their web application instantly support over 350 countries and languages with only a few lines of code. If your website has global customers, you need to adapt your website to them, and not make them adapt to your website. The Globalize plugin makes that easy, by using the power of client-side JavaScript.

Share:

Michael Abernethy, Programmer, Freelance

Mike AbernethyIn his 12 years in technology, Michael Abernethy has worked with a wide variety of technologies and a wide variety of clients. He focuses on building better and more complex web applications, testing the limits of the browsers they run on, while at the same time trying to figure out how to make them easier to create and easier to maintain. When he's not working at his computer, he can be found spending time with his kids.



05 August 2011

Also available in Chinese Russian Japanese

Introduction

It's time for another series of articles on jQuery! Since my last series, jQuery has solidified its position as the client-side JavaScript library of choice. This article focuses on the new Globalize plugin.

Wasn't this a jQuery plugin?

It was a jQuery plugin when Microsoft® first released it to jQuery. However, the jQuery UI team recently decided to make it available to all JavaScript coders, and all JavaScript frameworks. To do so they decoupled it from jQuery. First-time users can rest assured that what you're reading is the most current information regarding this plugin at the time of writing. Those who've tried the plugin before, feel comfortable knowing that very little has changed in the core aspects of the code — simply changing the "$" in your code to "Globalize" will likely solve most of your problems.

Globalization is a vital part to every website in today's world. Many could potentially be sacrificing business from international customers who don't understand or want to support one particular language. What if I told you that supporting any customer from any location in the world is now just as easy as not supporting them? Well, this article is here to teach you how.


Background of the problem

Not everyone in the world speaks the same language. Nor does everyone in the world format his or her numbers, dates, and currency the same way.

You may wonder why this is a problem now. Websites have been around for years now and different countries have been around for 10,000 years. The problem is becoming more acute because we are increasingly seeing websites do more work on the client, instead of relying on the server to do all the work, or relying on Ajax client-server calls to do the work. Developers want a pure-client solution for everything now.

Let's look at a simple example to prove my point. Say you are the owner of a web application called "Double It!". A user types a number into a text field, and in a SPAN below it, the number is doubled when the user presses a button. That seems simple enough, however, a user in the US would come to the site and type "1,250.25" into the text field and your application would spit out the response - "2,500.5". You could write some client-side JavaScript to do that easily.

$("#reponseSpan").text(2 * new Number($("#inputTextField").val()));

Now, a user from Germany comes to the site. They don't write their numbers in Germany the same way as in the US. The German types in "1.250,25". If you use the same JavaScript you used for your US client, you'll get a JavaScript error when it tries to create the Number object. Why would that happen? Because the built-in JavaScript functions expect numbers to be in US format, and because your German visitor doesn't type that way.

You could instruct users of your website to type all numbers in US format, but is that really the best answer? Do you want to force your users to do things your way, or do you want to accept whatever they do and make the user experience as nice as possible.

Table 1 shows examples of possible formats for numbers that exemplify why the Globalize plugin is the best solution.

Table 1. Number formats for different countries
Country Number format
United States1,250.25
Germany1.250,25
France1 250,25
Switzerland1'250,25

Cultures

How does the Globalize plugin deal with these aforementioned issues? They structure the solution in terms of cultures. A culture shouldn't be thought of as a language, because many countries in the world speak the same language, like Spanish. Likewise, a culture shouldn't be thought of as a country, since there are some countries that have multiple official languages, like Switzerland. Instead, the notion of a culture can be thought of as a unique combination of a country and a language. Thus, while Spanish isn't unique and Spain isn't unique, Spanish-Spain is unique, and thus can be considered a culture (to differentiate it from Spanish-Mexico and Catalan-Spain).

When you group together all the unique languages and countries in the world, there are approximately 350 cultures that need to be supported. (That's another reason you wouldn't even want to consider hacking together a solution yourself.) When the Globalize plugin constructs the culture, it uses two 2-letter codes. The first is a 2-letter lowercase language code, termed the ISO 639 code and the second is a 2-letter uppercase country code, termed the ISO 3166 code (see Resources). For example, "en-US" would denote that it's an English language and United States culture, and a "fr-FR" would denote a French language and France culture.

Also important is the "neutral" culture. In effect, the Globalize plugin says, "If you can't get specific language and country information, you can just pass the language, and we'll make a best guess as to how to properly format everything". This is a big help, as you'll see later. People browsing the web tend to identify the language they speak, but determining their country code can sometimes be very difficult.


Setting cultures in JavaScript

Now that you know the background on the problem and how the Globalize plugin uses the Culture object to abstract out the underlying details, let's see how you can set the culture when you're working with JavaScript.

The first step is to download the Globalize plugin itself (see Resources). Also note, that jQuery is not required to use the Globalize plugin. It was in the past, but the newest version doesn't require it. The Globalize plugin is designed so that the "main" plugin is in the root folder and called "globalize.js". This file is small (only 44 KB), but it only does American English. This is because it only contains the default values for all the functionality, and doesn't contain any actual culture code. Open the "cultures" folder and there are 353 files, which can add complexity to the problem. Now identify this special file: "globalize.cultures.js" — this file contains every possible culture so you don't have to load specifically the ones you need to work with. Of course, including every culture has its drawback in that it creates a large file, 828 KB. That's likely excessive in any production situation. However, it's good enough for us to write some example code for now.

Listing 1. Loading Globalize in test environments
// Remember, you should only load the globalize.cultures.js when testing and in example
       code like this
// Later sections will show how to dynamically load each Culture as needed
<script type="text/javascript" src="globalize.js"></script>
<script type="text/javascript" src="cultures/globalize.cultures.js"></script>

Setting the Culture within JavaScript is, like most things, very easy. Here are a few examples of how to set them.

Listing 2. Setting a Culture
// Remember, this only works if you include the globalize.js file!
// And, you HAVE to include the globalize.cultures.js file OR
// each individual culture's JS file

// You can set the culture directly by referencing its name
Globalize.culture = Globalize.cultures.de;

// You can set the culture directly by referencing it from
// the Cultures array
Globalize.culture = Globalize.cultures["de-DE"];

Formatting a number

Formatting and parsing are the two most fundamental functions when working with internationalization. After all, you're letting your users type in numbers and dates in the format they wish. Yet, you need to get their input into a format you can work with (parse), or display your own data in a format they want (format).

Formatting a number is taking a number, an actual number that can be stored as a Number object in JavaScript, and displaying it properly to the user in terms they are used to seeing numbers in. Recall the previous example of where an American may write "1,250.30" a German may write "1.250,30".

In most languages, formatting numbers involves working with four types of formats: numbers (the digits in front of a decimal point), decimals (the digits after a decimal point), percentage (which multiplies the number by 100 and shows the "%" sign) and currency (which shows that culture's currency symbol, like the $ or the Euro symbol). Each type of format can take a numerical argument as well, to tell the formatter how to properly apply the pattern.

  • n— is used for numbers, and denotes to the formatter to turn this Number object into a number. This argument can be used with a number, which tells the formatter how many decimal points to use. For example, "n3" will tell the formatter to make the argument a number with 3 decimal points.
  • d— is used for decimals, and denotes to turn this Number object into a number with decimals. This argument can be used with a number, which tells the formatter how many digits to use before the decimal point. For example, "d2" will the formatter to make the number forcibly have 2 digits to the left of the decimal point.
  • p— is the same as the "n" argument, except that the formatter multiplies the number by 100 and adds a "%" after the number.
  • c— is the currency symbol, and adds the culture's currency symbol to the appropriate spot. Adding a number argument as well will tell the formatter how many decimal digits to use.
Listing 3. Number formatting examples
// Assume that the correct Culture JS file is always added here

// make the Culture German
Globalize.culture = Globalize.cultures.de;

// test the "n" command
Globalize.format(1839.560, "n1"); // outputs 1.839,6
Globalize.format(1839.560, "n0"); // outputs 1.840
Globalize.format(1839.560, "n6"); // outputs 1.839,560000

// test the "c" command
Globalize.format(1839.560, "c2"); // outputs 1.839,56 €
Globalize.format(1839.560, "c3"); // outputs 1.839,560 €
Globalize.format(1839.560, "c6"); // outputs 1.839,560000 €

// now make it English and run the same code
Globalize.culture = Globalize.cultures.en;

// test the "n" command
Globalize.format(1839.560, "n1"); // outputs 1,839.6
Globalize.format(1839.560, "n0"); // outputs 1,840
Globalize.format(1839.560, "n6"); // outputs 1,839.560000

// test the "c" command
Globalize.format(1839.560, "c2"); // outputs $1,839.56
Globalize.format(1839.560, "c3"); // outputs $1,839.560
Globalize.format(1839.560, "c6"); // outputs $1,839.560000

There are a few things I don't like about the formatting functions used here, though. First, the currency formatting isn't flexible to allow me to specify where I want the currency symbol. Compare that to a formatter like Java™ uses, where the coder can specify where the currency symbol should go, for example, in front, in back, with a space, or without a space. It's possible with this Globalize plugin, but it requires either editing the "de" Culture object (not recommended) or creating a custom one. Additionally, Java formatting allows you to add characters to the beginning and end as further customization. For example, when you're talking a signing bonus for a new job, you may write your desired signing bonus as "$24k", with the "k" representing "thousands". This type of abbreviation is common in the US, with "k" for thousands, "M" for millions, and "B" for billions. Unfortunately, this is not possible in the Globalize plugin formatting right now, without writing your own Culture object to do so.

Ultimately, I do not think that the formatting is as powerful as Java on the server-side, but it's still a solution in nearly every use case you would need it.


Parsing a number

Once a user types in a number, currency, or percentage, you need to get that information, and you need it in a form that you can create a Number object with it. In other words, you need it in a way that the code can do math with it. In addition, you may want to get it in the proper form if you pass it to the server. In this case, you need to parse the number, turning it from a string into a number.

Listing 4. Number parsing examples
// Assume that the correct Culture JS file is always added here

// make the Culture German
Globalize.culture = Globalize.cultures.de;

// Call parseInt() on it, and get an object back we can work with
var num = Globalize.parseInt("1.839,56"); // will create an object with value 1840

// now make it English and run the same code
Globalize.culture = Globalize.cultures.en;

// Call parseInt() on it, and get an object back we can work with
var num = Globalize.parseInt("1,839.56"); // will create an object with value 1840

// Likewise, there's a parseFloat() that will preserve the decimal points
var num = Globalize.parseFloat("1,839.56"); // will create an object with value 1839.56

Formatting a date

Formatting is not just limited to numbers with the Globalize plugin. Dates can also be formatted. After all, the date March 8, 2011, can be written as 3/8/11 in the United States, and 8/3/11 in Germany. Formatting dates is much more complicated and involved than formatting numbers, and for that reason, I won't go into great detail here about all the options you can use with formatting a date. That's what the documentation is for after all.

Listing 5. Date formatting examples
// Assume that the correct Culture JS file is always added here

// make the Culture German
Globalize.culture = Globalize.cultures.de;

// Create a date for March 8th, 2011
// NOTE - months are 0 indexed, but the day isn't...how dumb!
Globalize.format(new Date(2011,2,8),"d"); // outputs 08.03.2011
Globalize.format(new Date(2011,2,8),"M"); // outputs 08 März
Globalize.format(new Date(2011,2,8),"D"); // outputs Dienstag, 8. März 2011

// now make it English and run the same code
Globalize.culture = Globalize.cultures.en;

Globalize.format(new Date(2011,2,8),"d"); // outputs 3/8/2011
Globalize.format(new Date(2011,2,8),"M"); // outputs March 08
Globalize.format(new Date(2011,2,8),"D"); // outputs Tuesday, March 08, 2011

Parsing a date

Parsing a Date object is just as easy.

Listing 6. Date parsing examples
// Assume that the correct Culture JS file is always added here

// make the Culture German
Globalize.culture = Globalize.cultures.de;

Globalize.parseDate("3/8/2011"); // creates a JavaScript Date object for August 3rd, 2011
Globalize.parseDate("Tuesday, March 08, 2011"); // returns null

// now make it English and run the same code
Globalize.culture = Globalize.cultures.en;

Globalize.parseDate("3/8/2011"); // creates a JavaScript Date object for March 8th, 2011
Globalize.parseDate("Tuesday, March 08, 2011"); // creates a JavaScript Date object for
          March 8th, 2011

Dynamic globalization

To this point, we've used the "globalize.cultures.js" file in our examples to make the code easier to work with. The "globalize.cultures.js" file contains all 350 cultures, and as a result, it's 828 KB. That's fine for these examples, but it will not work in a production environment. Why pass 349 unneeded cultures with every page load when you don't have to? Most of your users will only need one or two cultures loaded on their page in order to completely internationalize the page.

In addition, how do you even know which cultures should be loaded on a page? It's not as if a browser can tell you exactly where the user is coming from in the world (though that's changing with location-based services and with IP address lookups). If I travel with my laptop to Stockholm, I wouldn't want every site to automatically convert everything to Swedish formatting just because I've physically changed locations. I haven't physically changed the way I read numbers and dates just cause I've changed locations. Wouldn't it be nice if there was a way to automatically tell what languages and countries a user preferred as they surfed the web.

There is a way. The easiest way by far would be to have access to that information in JavaScript. Surprisingly, even though JavaScript gives you lots of information about the browser, it doesn't give you information about preferred languages. However, even though JavaScript doesn't give you access to that information, you can get it with a server-side language like Java or PHP.

Our goal is to determine what language(s) the user prefers. Then, based on those languages, we want to load only those cultures onto the page, in order to reduce traffic and page load time. Finally, we want to call Globalize.culture() with the users' preferred language, so that the Globalize plugin knows how to format everything. Listing 7 shows code in Java and Listing 8 shows code in PHP that you can use to internationalize your web page automatically.

If you use JSP or PHP, feel free to take this code and paste it into your own code if you plan to use the Globalize plugin. It's ready to deploy and has no external dependencies; Java 5 or higher, PHP 4 or higher.

Listing 7. Java internationalization code
<%
    // the user's preferred language is stored in the Header, and the Accept-Language
    // field
    // For example, in my Firefox browser, it shows 
    // en-us,en;q=0.5
    // This means my primary Culture is "en-us" which has a q=1.0
    // My backup Culture is "en" neutral, which has a q=0.5
    // q values are used to rank the cultures and are a system
    // for safe fail-over
    String header = request.getHeader("Accept-Language");
    
    // Split each language into separate locales
    String[] locales = header.split(",");

    // load the globalize.js file, which must always
    // be loaded to use the Globalize plugin
    out.println("<script src=\"globalize.js\"
                type=\"text/javascript\"></script>");

    // loop through each locale, and load the appropriate Globalize
    // plugin file.
    // for example, since I have 2 locales, it will load the
    // globalize.culture.en-US.js file and the
    // globalize.culture.en.js file
    for (int i=0; i<locales.length; i++)
    {
        int end = (locales[i].indexOf(";") == -1) ? locales[i].length() : 
          locales[i].indexOf(";");
        String locale = locales[i].substring(0,end);
        out.println("<script src=\"cultures/globalize.culture." +
                    locale + 
                    ".js\" type=\"text/javascript\"></script>");
    }

    // Finally, call culture() with the Accept-Language
    // The Globalize plugin accepts the String directly from
    // the Header, and deals with the q values appropriately,
    // even failing over safely on its own
    out.println("<script>$(document).ready(function(){Globalize.culture(\"" +
               header + 
               "\");});</script>");
%>
Listing 8. PHP internationalization code
// Here's the same thing in PHP
<?
$accept_language = $_SERVER["HTTP_ACCEPT_LANGUAGE"];

$languages = explode(",", $accept_language);

echo "<script src=\"globalize.js\" type=\"text/javascript\"></script>";

for each ($languages as $language) {
        $locale = explode(";", $language);

        echo "<script src=\"cultures/globalize.culture.".$locale[0].".js\"
          type=\"text/javascript\"></script>";
}

echo '<script>$(document).ready(function(){Globalize.culture
          ("'.$accept_language.'");});</script>';
?>

Conclusion

The Globalize plugin is a great new addition to the jQuery and JavaScript arsenal. It's the result of years of work from Microsoft, who donated their work to the jQuery community and from which we can all benefit. The plugin is definitely all encompassing, as it includes 350 unique language-country cultures. Literally, everyone in the world who visits your site will be covered by the Globalize plugin. That should be enough to convince you to use this Globalize plugin instead of any other homemade plugin you may have tried. (Ironically, although I wrote the a popular jQuery number formatting plugin for a previous article on developerWorks, I'm telling you to stop using that one, and use this one instead.)

The Globalize plugin allows you to easily format numbers and dates, and it allows you to parse those values as well. This plugin can be used to customize the way a web page displays its numbers and dates and allow your web pages to connect to your users better, by taking another vital step in the "make your web app look more like a desktop app". Previously, if developers wanted to format numbers and dates correctly for each culture, they were forced to do Ajax client-server calls, or do all the processing on the server directly. As we're seeing with all current web applications, more of the processing and logic is now taking place on the client with JavaScript.

We did not go into some of the more detailed aspects of the Globalize plugin. For example, the plugin is adaptable enough that you can easily write your own culture and plug that into the system. However, because we know there are 350 cultures already included with the plugin, are you really in need of another one? If you think you are, it's easy to do, but I didn't see that as a necessary topic in this article.

Finally, I showed you Java and PHP code that could dynamically determine the correct cultures your visitors prefer to use with their browser, and then dynamically load the correct culture files to include with the Globalize plugin, and finally how to dynamically call culture() to set those cultures on the page. You should be able to simply cut and paste the above code into your own code to internationalize your web pages.

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Open source on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source
ArticleID=750525
ArticleTitle=New things with jQuery, Part 1: Globalize
publish-date=08052011