Sep 2009 11

jQuery.i18n.properties

About


jQuery.i18n.properties is a lightweight jQuery plugin for providing internationalization to javascript from ‘.properties’ files, just like in Java Resource Bundles. It loads and parses resource bundles (.properties) based on provided language and country codes (ISO-639 and ISO-3166) or language reported by browser.


Resource bundles are ‘.properties‘ files containing locale specific key-value pairs. The use of ‘.properties‘ files for translation is specially useful when sharing i18n files between Java and JavaScript projects. This plugin loads the default file (eg, Messages.properties) first and then locale specific files (Messages_pt.properties, then Messages_pt_PT.properties), so that a default value is always available when there is no translation provided. Translation keys will be available to developer as javascript variables/functions (functions, if translated value contains substitutions (eg, {0}) or as a map.


This plugin was inspired on the Localisation assistance for jQuery from Keith Wood, and is made available under a dual license (GPL and MIT).



Features


  • Use Java standard ‘.properties‘ files for translations
  • Use standard ISO-639 for language code and ISO-3166 for country code
  • Sequential loading of resource bundles from base language to user-specified/browser-specified so there is always a default value for an untranslated string (eg: msg.properties, msg_pt.properties, msg_pt_PT.properties)
  • Use browser reported language if no language was specified
  • Placeholder substitution in resource bundle strings (eg, msg_hello = Hello {0}!!)
  • Suport for namespaces in keys (eg, com.company.msgs.hello = Hello!)
  • Resource bundle keys available as Javascript vars/functions or as a map



Example


Take as an example the following Messages.properties, Messages_pt.properties and Messages_pt_PT.properties:

Messages.properties
1
2
3
4
# This line is ignored by the plugin
msg_hello = Hello
msg_world = World
msg_complex = Good morning {0}!
Messages_pt.properties
1
2
# We only provide a translation for the 'msg_hello' key
msg_hello = Bom dia
Messages_pt_PT.properties
1
2
# We only provide a trnaslation for the 'msg_hello' key
msg_hello = Olá

Now, suppose these files are located on the ‘bundle/‘ folder. One can invoke the plugin like below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// This will initialize the plugin
// and show two dialog boxes: one with the text "Olá World"
// and other with the text "Good morning John!"
jQuery.i18n.properties({
    name:'Messages',
    path:'bundle/',
    mode:'both',
    language:'pt_PT',
    callback: function() {
        // We specified mode: 'both' so translated values will be
        // available as JS vars/functions and as a map

        // Accessing a simple value through the map
        jQuery.i18n.prop('msg_hello');
        // Accessing a value with placeholders through the map
        jQuery.i18n.prop('msg_complex', ['John']);

        // Accessing a simple value through a JS variable
        alert(msg_hello +' '+ msg_world);
        // Accessing a value with placeholders through a JS function
        alert(msg_complex('John'));
    }
});

This will initialize the plugin (loading bundle files and parsing them) and show a dialog box with the text “Olá World” and other with “Good morning John!”. The english word “World” is shown because we didn’t provide a translation for the msg_world key. Also notice that keys are available as a map and also as javascript variables (for simple strings) and javascript functions (for strings with placeholders for substitution).



Usage

Options

Option Description Notes
name Partial name (or names) of files representing resource bundles (eg, ‘Messages’ or ['Msg1','Msg2']) Required
String or String[]
language ISO-639 Language code and, optionally, ISO-3166 country code (eg, ‘en’, ‘en_US’, ‘pt_PT’). If not specified, language reported by the browser will be used instead. Optional
String
path Path to directory that contains ‘.properties‘ files to load. Optional
String
mode Option to have resource bundle keys available as Javascript vars/functions OR as a map. The ‘map’ option is mandatory if your bundle keys contain Javascript Reserved Words. Possible options: ‘vars’ (default), ‘map’ or ‘both’ Optional
String
callback Callback function to be called uppon script execution completion. Optional
function()

Including and invoking the plugin

  1. Load the script:
    1
    2
    <script type="text/javascript" language="JavaScript"
      src="js/jquery.i18n-1.0.3-min.js"></script>
  2. Initialize the plugin (minimal usage, will use language reported by browser):
    1
    jQuery.i18n.properties({name: 'Messages'});
  3. Access a translated value (assuming a key named ‘org.somekey‘ exists):
    1
    alert( org.somekey );



Download




Change Log


1.0.4  [2009-12-29]
  • When using the map approach to retrieve bundle values, unicode chars may not be properly unescaped
1.0.3  [2009-10-06]
  • When using the map approach to retrieve bundle values, if there’s no value for a specified key, key is returned (previously, null was returned)
  • Fixed lot of errors accordingly to JSLint
1.0.2  [2009-09-18]
  • Option to have resource bundle keys available as Javascript vars/functions AND/OR as a map. The later is mandatory if your bundle keys contain Javascript reserved words
1.0.1  [2009-09-14]
  1. nfgrilo (Nuno Fernandes)

    Released jQuery plugin for I18N from .properties files, with support for placeholder subsititutions: http://codingwithcoffee.com/?p=272 #fb

  2. Lovemore Nalube

    This is fantastic work! I am trying it out in Sakai’s Course Evaluation Tool @ http://jira.sakaiproject.org/browse/EVALSYS-795

  3. Lovemore Nalube

    Hi, I’ve had little success implementing this so far. The following issue up:

    The path bundle/messages.properties is not made available for direct access by tomcat restrictions. Hence only an RSF error page is called with no translation strings in it. Hence this parse error is thrown by jquery.i18n:

    syntax error typeof <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1 == "undefined"

  4. Nuno

    Hi Lovemore!

    One approach you may try is to use the ’maven-resources-plugin’ to copy your bundle files at compile time to a tool webapp folder (accessible location by the webserver).

    In the base pom.xml of the tool:
      <build>
      <pluginManagement> 
        <plugins> 
          <plugin> 
            <groupId>org.apache.maven.plugins</groupId> 
            <artifactId>maven-resources-plugin</artifactId> 
            <version>2.4</version> 
          </plugin> 
        </plugins> 
      </pluginManagement> 
      </build>

    In the tool/pom.xml:
      <build>
        <!– …….. –>
        <plugin>
    <groupId>org.apache.maven.plugins</groupId> 
            <artifactId>maven-resources-plugin</artifactId> 
            <executions>
              <execution>
                <id>copy-resources</id>
                <phase>process-resources</phase>
                <goals><goal>resources</goal></goals>
                <configuration>
                  <outputDirectory>${project.build.directory}/${project.build.finalName}/bundle</outputDirectory>
                  <resources>          
                    <resource>
    <!– Change THIS to get to you actual folder containing the .properties files –>           <directory>${basedir}/../bundle/src/resources</directory>
                    </resource>
                  </resources>              
                </configuration>            
              </execution>
            </executions>
          </plugin>
          
        </plugins>
        
      </build>

    Hope this helps!
    Please let me know if it solved your issue!

    Nuno

  5. Lovemore Nalube

    Hey that worked! JS can access the properties file now. But there’s a bug with parsing the file:

    not well-formed:
    [Break on this error] # Application messages file – Sakai Evaluation System -AZ

    the comments within the properties file prefixed with # throw this exception and the plugin just gives up.

    Any thoughts?

  6. Nuno

    Hi Lovemore!
    I have already seen that message – is it from Firebug 1.4? I have a lot of things (like JS templates) that firebug reports as errors but everything is working fine. Before considering this a bug, can you please test this (replacing with the correct values):

    jQuery.i18n.properties({name:’messages’, path:’bundle/’, language:’en’, callback: function() {
    // replace by a parsed key from the bundle
    alert(msg_some_key);
    }});

    If that doesn’t work, can you please send me your .properties file so I can test it locally? Just send me using the contact form on the top of this page :)

    Thanks,
    Nuno

  7. Nuno

    Not sure if I have already update Firebug meanhile (now have 1.4.2) because similar error have gone from my console. Testing a .properties file with that comment line works fine for me…

    Nuno

  8. Lovemore Nalube

    Here is the properties file: https://source.sakaiproject.org/contrib/evaluation/trunk/tool/src/java/org/sakaiproject/evaluation/tool/bundle/messages.properties

    The callback function does not execute.
    Firebug is 1.4.2

    Thanks
    Lovemore

  9. Nuno

    I found the issue: since the bundle keys are available as javascript variables/functions, some do fail because Javascript reserved words cannot be used!

    http://javascript.about.com/library/blreserved.htm

    I will modify the plugin to have an option of accessing the bundle keys through an array (map) and will add a comment when ready!

    Nuno

  10. Nuno

    Hi Lovemore!

    In your case, since Javascript reserved words are being used inside the .properties file (delete), you must download the 1.0.2 version and specify the option mode:’map’. Example:

    jQuery.i18n.properties({name:’messages’, path:’bundle/’, language:’en’, mode:’map’, callback: function() {
    // replace by a parsed key from the bundle
    alert( jQuery.i18n.prop(‘msg_some_key’) );
    }});

    Hope this helps,
    Nuno

  11. Lovemore Nalube

    Hi Nuno :)

    A while back I had an idea of a twist to your idea. I’ve detailed it here: http://blogs.uct.ac.za/blog/lovemores-world/2009/09/28/consuming-a-sakai-java-messages-resource-bundle-in-javascript-with-fluid-i8n

    Would love to know what you think. My code is running in the Sakai Course Evaluations tool :)

  12. shotchen

    please try this:
    layout.close_pane = \u5173\u95ED
    alert(jQuery.i18n.prop(“layout.close_pane”));
    alert(layout.close_pane);

    first alert is show \u5173\u95ED and second alert will show 关闭 ,why?

  13. Jason Shao

    I dig this – though I wish there was an easier way to over-ride w/a db as a message source, to support easy GUI customization of bundles w/o writing to files.

    Dynamically generate the .properties files using a servlet that did dynamic db overlay ontop of the underlying file? (we use something similar for portlets – we standardized on tags to make db slightly easier as a message source.

  14. Nuno

    Hi Jason!
    If what you’re trying to achieve is some kind of dynamic generation of bundles (server-side) in Sakai, one possible approach (and alternative to this plugin) is to use EntityBroker RESTfull capability to return either a ResourceLoader object or a map {key,translation}. This way, you can simply query server through ajax and restfull urls to retrieve the results as a Javascript map (JSON)! Entitybroker return results encoded in JSON automatically.
    Is this your objective?

    Thank you for your thoughts,
    Nuno

  15. Nuno

    Hi Shotchen!

    Thank you for reporting the issue! I have just updated the plugin to fix it (1.0.4). Thank you very much, Nuno

Leave a Reply