JsWorld

Library Design and Features

1. Clean separation between formatting logic and locale data

The JsWorld library makes a clean separation between formatting logic and locale data. So, on one hand you've got the generic classes for formatting and parsing numbers, money and dates/times, and on the other -- a set of loadable data files describing the formatting rules for each supported locale. You as a user have freedom to easily fine-tune formatting to your particular needs and I, as a maintainer of the library, can quickly update it as newer locale data is published by the Unicode Consortium.

Separation of logic and data

The formatting classes are located in file JsWorld.js (and the minified JsWorld.min.js), while the definitions for the 300+ locales are put into files in the locales/ directory.

2. Use of POSIX-style locale properties

Care was taken to keep things standard and unsurprising. To describe the formatting conventions of the various locales JsWorld follows the POSIX specification, which should be well known to OS and systems programmers. And while the POSIX locale definition standard may have some shortcomings, it is good enough for most localisation purposes.

Category POSIX specification
Numeric formatting LC_NUMERIC
Monetary formatting LC_MONETARY
Date/time formatting LC_TIME

If you open one of the locale data files in the locales/js/ directory, you'll see a JavaScript object with the various POSIX LC_NUMERIC, LC_MONETARY and LC_TIME properties. Here is the file storing the properties for the French locale:

locales/js/fr_FR.js

if(typeof POSIX_LC == "undefined") var POSIX_LC = {};

POSIX_LC.fr_FR = {
	"decimal_point"      : ",",
	"thousands_sep"      : "\u00a0",
	"grouping"           : "3",
	"abday"              : ["dim.","lun.","mar.",
	                        "mer.","jeu.","ven.",
				"sam."],
	"day"                : ["dimanche","lundi","mardi",
	                        "mercredi","jeudi","vendredi",
				"samedi"],
	"abmon"              : ["janv.","f\u00e9vr.","mars",
	                        "avr.","mai","juin",
				"juil.","ao\u00fbt","sept.",
				"oct.","nov.","d\u00e9c."],
	"mon"                : ["janvier","f\u00e9vrier","mars",
	                        "avril","mai","juin",
				"juillet","ao\u00fbt","septembre",
				"octobre","novembre","d\u00e9cembre"],
	"d_fmt"              : "%d/%m/%y",
	"t_fmt"              : "%H:%M:%S",
	"d_t_fmt"            : "%e %B %Y %H:%M:%S %Z",
	"am_pm"              : ["AM","PM"],
	"int_curr_symbol"    : "EUR ",
	"currency_symbol"    : "\u20ac",
	"mon_decimal_point"  : ",",
	"mon_thousands_sep"  : "\u00a0",
	"mon_grouping"       : "3",
	"positive_sign"      : "",
	"negative_sign"      : "-",
	"int_frac_digits"    : 2,
	"frac_digits"        : 2,
	"p_cs_precedes"      : 0,
	"n_cs_precedes"      : 0,
	"p_sep_by_space"     : 1,
	"n_sep_by_space"     : 1,
	"p_sign_posn"        : 1,
	"n_sign_posn"        : 1,
	"int_p_cs_precedes"  : 0,
	"int_n_cs_precedes"  : 0,
	"int_p_sep_by_space" : 1,
	"int_n_sep_by_space" : 1,
	"int_p_sign_posn"    : 1,
	"int_n_sign_posn"    : 1
};

Note that all non-ASCII characters have been replaced with the appropriate Unicode escape sequences.

The locale data is used to construct a jsworld.Locale object which is then required to create a corresponding formatting or parsing object.

// Create a new locale object from the fr_FR locale data
var lc = new jsworld.Locale(POSIX.fr_FR);

// Print the current date in French
var formatter = new jsworld.DateTimeFormatter(lc);
document.writeln(formatter.formatDate(new Date()));

// Print the current time in French
document.write(formatter.formatTime(new Date()));

The meaning of the individual locale properties is described in the corresponding POSIX specifications (see LC_NUMERIC, LC_MONETARY and LC_TIME) as well as in JsWorld's API docs.

3. Locale data formats

Depending on the type of your application, you have a choice between three different formats for locale data.

Locale data format File location Suitable for
JavaScript locales/js/ Static include in HTML docs via <script> tag
JSON locales/json/ Dynamic loading via XMLHTTPRequest
Mozilla properties locales/mozilla/ Mozilla XULRunner applications

3.1 JavaScript

For common web applications you should use the regular JavaScript data files in the locales/js/ directory. Use a <script> tag to include them, which places an object "POSIX_LC.<locale_code>" into your JavaScript namespace. The namespacing is done in way which allows you to include multiple locale definitions, without causing clashes.

<!-- Include the JsWorld formatting classes -->
<script type="text/javascript" src="JsWorld.js"></script>

<!-- Include the en_US locale data -->
<script type="text/javascript" src="locales/js/en_US.js"></script>

<!-- Include the de_DE (German) locale data -->
<script type="text/javascript" src="locales/js/de_DE.js"></script>


<script>
// Create formatter for US locale
var usLocale = new jsworld.Locale(POSIX_LC.en_US);
var usNumericFormatter = new jsworld.NumericFormatter(usLocale);

// Create formatter for German locale
var germanLocale = new jsworld.Locale(POSIX_LC.de_DE);
var germanNumericFormatter = new jsworld.NumericFormatter(germanLocale);

// Show number in US locale
alert(usNumericFormatter(25000.10));

// Show number in German locale
alert(germanNumericFormatter(74999.90));
</script>

You also have the option to include the definitions of all available locales (over 300) by pointing to the locales/js/POSIX_LC.js file. This, however, will consume a lot more memory and bandwidth as the aggregate data for all locales takes up about 500KB.

<!-- Include data for all available locales -->
<script type="text/javascript" src="locales/js/POSIX_LC.js"></script>

3.2 JSON

For highly interactive web applications (also known as AJAX or Web 2.0) you may look at the locale data files in the locales/json/ directory. They are in the form of JSON, making them suitable for dynamic loading via XMLHTTPRequest.

<!-- Include the JsWorld formatting classes -->
<script type="text/javascript" src="JsWorld.js"></script>

<script>
// Example assumes Firefox 3.5!

// The locale data file (e.g. Canadian English) in JSON format
var url = "http://my-site.com/jsworld/locales/json/en_CA.json";

// Make request
var req = new XMLHttpRequest();  
req.open("GET", url, true);  
req.onreadystatechange = function (aEvt) {  
    if (req.readyState == 4) {  
        if(req.status == 200) {
	
	    // Parse response JSON
	    var localeData = JSON.parse(req.responseText);
	    
	    // Create new formatter with received locale properties
	    var lc = new jsworld.Locale(localeData);
	    var dateTimeFormatter = new jsworld.DateTimeFormatter(lc);
	}
        else {
           alert("Error loading locale data");
	}
   }  
};  
req.send(null); 

</script>

3.3 Mozilla property files

Finally, the locale data is also made available as Mozilla property files. These are located in the locales/mozilla/ directory. The format is suitable for localising XULRunner applications and Firefox add-ons.

Here is a useful example how to import all items from a Mozilla property file and pack them into a single JavaScript object:

// XPCOM shorthands
const Cc = Components.classes;
const Ci = Components.interfaces;

// Location of the properties file
var stringBundleURI = "chrome://app/locale/en_US.properties";

// Open properties file
var stringService = Cc["@mozilla.org/intl/stringbundle;1"]
	.getService(Ci.nsIStringBundleService);
var sb = stringService.createBundle(stringBundleURI);

// Retrieve locale data
var localeProps = {};
var propertyList = sb.getSimpleEnumeration();

while (propertyList.hasMoreElements()) {
	var prop = propertyList.getNext().QueryInterface(Ci.nsIPropertyElement);
	localeProps[prop.key] = prop.value;
}

// Create locale object from the raw properties
var lc = new jsworld.Locale(localeProps);

// Create the corresponding formatters
var nf = new jsworld.NumericFormatter(lc);
var mf = new jsworld.MonetaryFormatter(lc);
var dtf = new jsworld.DateTimeFormatter(lc);

4. Exception handling

The formatting classes will raise an exception if something goes wrong, e.g. on:

You can handle these error conditions by using a try-catch clause:

try {
	var lc = new jsworld.Locale(POSIX_LC.en_GB);
	var mf = new jsworld.MonetaryFormatter(lc);
	
	// attempt to format an invalid amount
	mf.format("abc");
	
} catch (error) {
	alert(error);
}

5. Character encoding issues

Do you see garbled output in the browser? Most probably the cause is character encoding.

6. API documentation

Auto-generated API documentation is provided with the package as well as here online:

JsWorld [jsdoc]

7. Platforms

Here is a list of all platforms where the JsWorld library has been successfully tested: