In my previous post I showed a little proof-of-concept for an httphandler which I want to use to dynamically get the localization resources from SharePoint as javascript object. But wait a moment. How does SharePoint handle localization on client? When you look in Script tab in Chrome dev tools, you’ll find:
ScriptResource.axd
It is added to the server when deployed. See a good introduction to WebResource.axd and ScriptResource.axd by Brian Chavez.
ScriptRe
sx.ashx
That was new. Try to google it. You won’t find much results.

Wait a sec, so there is actually the localization httphandler in SharePoint which I wanted to do?
Can’t be true, can it? Let’s look closer. So the url for this javascript file is:
/_layouts/ScriptResx.ashx?culture=nb-no&name=SP.Res
It is added with RegisterSod:
<script type="text/javascript">
RegisterSod("sp.res.resx",
"\u002f_layouts\u002fScriptResx.ashx?culture=nb\u00252Dno\u0026name=SP\u00252ERes\u0026rev=4rLK\u00252F2Oo4ectujVWgrynrA\u00253D\u00253D");
</script>
It begins by:
_EnsureJSNamespace('SP');
SP.Res={lcid:'1044',autocompleteAccessibleMenuName:'Autofullf\u00F8r-meny'....

Very nice. But where resides this SP.Res?… Perhaps, it isn’t a file, rather a name, that the javascript code is encapsulated within. The javascript object is called SP.Res.
By the way, a nice usage of it is to define the current language:
var lang = SP.Res.lcid;
ScriptResx.ashx file is located in Layouts folder and contains only one line:
<%@ WebHandler
Language="C#" Class="Microsoft.SharePoint.ApplicationPages.ScriptResxHandler,Microsoft.SharePoint.ApplicationPages,Version=14.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c"
%>
So the class is Microsoft.SharePoint.ApplicationPages.ScriptResxHandler. Google gives no results… The Microsoft.SharePoint.ApplicationPages.dll resides in C:\inetpub\wwwroot\wss\VirtualDirectories\80\_app_bin
This is what .NET Reflector reveals:

The file is loaded is actually 14/Resources/SP.Res.resx:

Usage for other and custom .resx files
Wow, those who read all this, you’ll be awarded: You can actually load any localization files with ScriptResx.ashx (anything which can be found in 14/Resources/-folder, core, spscore and much more, or your own custom resx-files). Just add your script:
<script src="/_layouts/ScriptResx.ashx?culture=nb-no&name=Takana></script>
This will be loaded into “Res”.
One shortcoming is that multiple resx-files would override each other because every localization file loads properties into “Res” object (except SP.Res). Another shortcoming is that we can’t load culture-invariant localization files, because the filePath concatenation is much hard coded: It depends on a dot and culture-name:

Get Culture dynamically
ScriptResx.ashx depends on culture name. Sure we can put this directly in the script tag. But what if you can’t know the current language of the site? It is not so difficult actually, just get the current culture name in the asp.net markup:
<script
type="text/javascript"
src="/_layouts/ScriptResx.ashx?culture=<%= System.Globalization.CultureInfo.CurrentCulture.Name %>&name=Takana">
</script>
Update: An even better solution is to use L_Menu_LCID
(found on Ruud Heemskerk’s Blog):
<script
type="text/javascript"
src="/_layouts/ScriptResx.ashx?culture=" + L_Menu_LCID + "&name=Takana">
</script>
Resolving name conflict
If you load multiple localization files, all will use “Res” name for the localization object and override the latest localizations. To solve it we must catch the Res object and encapsulate it within another object which we have control over. Here we can use many techniques, among them ExecuteOrDelayUntilScriptLoaded (with RegisterSod and LoadSod) from SharePoint, we can use head.js to load and run a callback. But the simplest way is to use jQuery.getScript function:
jQuery.getScript("/_layouts/scriptresx.ashx?culture=nb-no&name=Takana")
.done(function() { Takana = {}; Takana.Res = Res; })
.fail(function() {console.log("error");});
Update 2013-02-01
I found a better way to resolve the name conflict for resx-files loaded with scriptresx.ashx. Just update the classFullName resheader in your resx file. Details in my new post:
Recent Comments