At work we are using a lot of JavaScript for all of our user controls and other ASP.NET components. I’m guessing that so are you. Our solution is to add a .js file per each user control and then load them on the page dynamically as explained here. That is a great way to componentize your web application.

Bad behavior

The problem is when an action on one user control’s JavaScript effects elements on other user controls. An example could be the page header where it says Signed in as Joe. When you are on the update profile page and change your name from Joe to Johnny using AJAX, then you don’t update the header element. If you do, you probably reference the DOM element from your profile update script directly. This is bad since the header and profile are located in two different user controls and thus haven’t got a clue about the existence of each other. Why should the JavaScript treat them differently?

Good behavior

What you really want to do is to use an event model. Then it works like so: 

The user changes his name to Johnny and the AJAX function in JavaScript triggers an event called profileNameUpdated and passes the new name along as a parameter. The page header have already told the event model to subscribe to the profileNameUpdated event and so it now catches the event triggered by the profile. It reads the new name and updates its own DOM element.

The code

Amazingly, the JavaScript code for this is less than 1KB and works on all websites, platforms and browsers. This is what it looks like.

// The constructor of the eventFramework
function eventFramework()
{
 this.handlers = [];
}

// Triggers the event specified by the name and passes the eventArgs to listeners
eventFramework.prototype.trigger = function(name, eventArgs)
{     
  for(var i = 0; i < this.handlers.length; i++)
  {
  if(this.handlers[i].eventName == name)
   this.handlers[i].eventHandler.call(this, eventArgs);
 }
}

// Adds a listener/subscriber to the event specified by the 'name' parameter
eventFramework.prototype.addListener = function(name, handler)
{
  if(typeof(name) != 'string' || typeof(handler) != 'function')
  {
  throw new SyntaxError("Invalid parameters when creating listener with the following arguments: 'Name': " + name + ", 'Handler': " + handler);
  }
 
  this.handlers.push({ "eventName" : name, "eventHandler" : handler });
}

// Initializes the eventFramework and makes it available globally
var events = new eventFramework();

Implementation

Download the .js file below and add it to your site. It should be the first JavaScript to be included in the <head> element of your pages.

When the script is included, it is now possible to start triggering and listening to events.  To trigger an event, simple write this:

events.trigger('profileNameUpdated', 'Johnny');

You can also pass objects or JSON as a parameter like so:

events.trigger('profileNameUpdated', {'name':'Johnny', 'oldName':'Joe'});

To subscribe or listen to these events, you simply add the following to any user control or JavaScript file:

events.addListener('profileNameUpdated', eventListener);

function eventListener(eventArgs)
{
  alert(eventArgs);
}

The eventArgs parameter will contain whatever was passed along by the trigger.

Download

eventmodel.js (966,00 bytes)

Just about every web project I’ve been involved in have, at one time or the other, needed to present some text to the visitor through JavaScript. It could be in an alert box or some other way and the problem has always been to localize that text into different languages using resource files or satellite assemblies.

There are many ways of localizing the keys, but most of them involve writing out variables on a page with the localized text and then let the .js include files read from those variables. That’s not a good solution. It would be much better if the .js files could be rendered with the localized text directly.

At work, I’ve written exactly such a mechanism and here is a cleaned up, plug ‘n play version you can use in your own web project. It’s an HttpHandler that intercepts the requests to the .js files and performs the localization based on regular expressions. Here’s how it works.

Resource files

It doesn’t matter whether you use .resx files or satellite assemblies for localizing your text, because both methods work with the System.Resources.ResourceManager class.

A resource file contains keys and a values. The key is always the same, but the value varies for each language. Each language is represented in its own .resx file and the name of the file decides which language it contains like below.

 

If no language is added in the file name, it automatically becomes the default localizations – normally English. The three .resx files each contain one line with the key nameOfPage. The value in each of the files is localized text that can be referenced by the key.

text.resx: Name of page
text.da.resx: Sidens navn
text.es.resx: Namo de la pago (Sorry, my Spanish is slightly rusty)

The script method

In the .js script files you need a way to specify a certain text as localizable. I’ve chosen to pick a syntax that looks like this:

<script type="text/javascript">
  alert(‘Translate(nameOfPage)’);
</script>

Notice that the Translate method above takes the localizable key as parameter. This is the syntax the regular expression in the HttpHandler is looking for.

The HttpHandler

The HttpHandler does a couple of things. It reads the content of the script file, localizes the text, caches the response and compresses it using HTTP compression. It does all this in about 150 lines of easy readable code.

To make it work, you need to update your script references a little bit. Instead of pointing to /scripts/messages.js you need to append .axd to the end of the reference so it becomes /scripts/messages.js.axd. Then you need to add this line to the web.config so that the new script reference actually works.

<httpHandlers>
  <add verb="*" path="*.js.axd" type="ScriptTranslator" />
</httpHandlers>

If your site isn’t yet localized you probably need to add the uiCulture attribute to the globalization element in the web.config and set its value to auto.

<globalization uiCulture="auto" />

This will trigger the correct .resx file to be used based on the visiting browser’s language. If your .resx files aren’t called text.resx or you use satellite assemblies, then you need to update the instantiation of the ResourceManager in the TranslateScript method in the handler.

Download code and sample

Download the code below and place the ScriptTranslator.cs in your App_Code folder. Then update your web.config with the web.config values found in the zip file. If you unzip the zip file, the entire contents can be opened directly in Visual Studio and you will be able to try it out easily.

Localization.zip (8,37 kb)