Localize Strings in Javascript

I'm currently using .resx files to manage my server side resources for .NET.

the application that I am dealing with also allows developers to plugin JavaScript into various event handlers for client side validation, etc.. What is the best way for me to localize my JavaScript messages and strings?

Ideally, I would like to store the strings in the .resx files to keep them with the rest of the localized resources.

I'm open to suggestions.


Solution 1:

A basic JavaScript object is an associative array, so it can easily be used to store key/value pairs. So using JSON, you could create an object for each string to be localized like this:

var localizedStrings={
    confirmMessage:{
        'en/US':'Are you sure?',
        'fr/FR':'Est-ce que vous êtes certain?',
        ...
    },

    ...
}

Then you could get the locale version of each string like this:

var locale='en/US';
var confirm=localizedStrings['confirmMessage'][locale];

Solution 2:

Inspired by SproutCore You can set properties of strings:

'Hello'.fr = 'Bonjour';
'Hello'.es = 'Hola';

and then simply spit out the proper localization based on your locale:

var locale = 'en';
alert( message[locale] );

Solution 3:

After Googling a lot and not satisfied with the majority of solutions presented, I have just found an amazing/generic solution that uses T4 templates. The complete post by Jochen van Wylick you can read here:

Using T4 for localizing JavaScript resources based on .resx files

Main advantages are:

  1. Having only 1 place where resources are managed ( namely the .resx files )
  2. Support for multiple cultures
  3. Leverage IntelliSense - allow for code completion

Disadvantages:

The shortcomings of this solution are of course that the size of the .js file might become quite large. However, since it's cached by the browser, we don't consider this a problem for our application. However - this caching can also result in the browser not finding the resource called from code.


How this works?

Basically he defined a T4 template that points to your .resx files. With some C# code he traverses each and every resource string and add it to JavaScript pure key value properties that then are output in a single JavaScript file called Resources.js (you can tweak the names if you wish).


T4 template [ change accordingly to point to your .resx files location ]

<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ assembly name="System.Windows.Forms" #>
<#@ import namespace="System.Resources" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.IO" #>
<#@ output extension=".js"#>
<#
 var path = Path.GetDirectoryName(Host.TemplateFile) + "/../App_GlobalResources/";
 var resourceNames = new string[1]
 {
  "Common"
 };

#>
/**
* Resources
* ---------
* This file is auto-generated by a tool
* 2012 Jochen van Wylick
**/
var Resources = {
 <# foreach (var name in resourceNames) { #>
 <#=name #>: {},
 <# } #>
};
<# foreach (var name in resourceNames) {
 var nlFile = Host.ResolvePath(path + name + ".nl.resx" );
 var enFile = Host.ResolvePath(path + name + ".resx" );
 ResXResourceSet nlResxSet = new ResXResourceSet(nlFile);
 ResXResourceSet enResxSet = new ResXResourceSet(enFile);
#>

<# foreach (DictionaryEntry item in nlResxSet) { #>
Resources.<#=name#>.<#=item.Key.ToString()#> = {
 'nl-NL': '<#= ("" + item.Value).Replace("\r\n", string.Empty).Replace("'","\\'")#>',
 'en-GB': '<#= ("" + enResxSet.GetString(item.Key.ToString())).Replace("\r\n", string.Empty).Replace("'","\\'")#>'
 };
<# } #>
<# } #>

In the Form/View side

To have the correct translation picked up, add this in your master if you're using WebForms:

<script type="text/javascript">

    var locale = '<%= System.Threading.Thread.CurrentThread.CurrentCulture.Name %>';

</script>

<script type="text/javascript" src="/Scripts/Resources.js"></script>

If you're using ASP.NET MVC (like me), you can do this:

<script type="text/javascript">

    // Setting Locale that will be used by JavaScript translations
    var locale = $("meta[name='accept-language']").attr("content");

</script>

<script type="text/javascript" src="/Scripts/Resources.js"></script>

The MetaAcceptLanguage helper I got from this awesome post by Scott Hanselman:

Globalization, Internationalization and Localization in ASP.NET MVC 3, JavaScript and jQuery - Part 1

public static IHtmlString MetaAcceptLanguage<T>(this HtmlHelper<T> html)
{
     var acceptLanguage =
         HttpUtility.HtmlAttributeEncode(
                     Thread.CurrentThread.CurrentUICulture.ToString());

      return new HtmlString(
      String.Format("<meta name=\"{0}\" content=\"{1}\">", "accept-language",
                    acceptLanguage));
 }

Use it

var msg = Resources.Common.Greeting[locale];
alert(msg);

Solution 4:

With a satellite assembly (instead of a resx file) you can enumerate all strings on the server, where you know the language, thus generating a Javascript object with only the strings for the correct language.

Something like this works for us (VB.NET code):

Dim rm As New ResourceManager([resource name], [your assembly])
Dim rs As ResourceSet = 
    rm.GetResourceSet(Thread.CurrentThread.CurrentCulture, True, True)
For Each kvp As DictionaryEntry In rs
    [Write out kvp.Key and kvp.Value]
Next

However, we haven't found a way to do this for .resx files yet, sadly.

Solution 5:

JSGettext does an excellent job -- dynamic loading of GNU Gettext .po files using pretty much any language on the backend. Google for "Dynamic Javascript localization with Gettext and PHP" to find a walkthrough for JSGettext with PHP (I'd post the link, but this silly site won't let me, sigh...)

Edit: this should be the link