Localization

Since OpenSocial is a global specification, with implementors in several countries, it is definitely worth the effort to localize your apps for as many markets as you can. Localization is the process of translating your app and otherwise tailoring it for a wider audience in different localities.

Gadgets have a built-in mechanism for localization called message bundles. Message bundles make it possible to tailor your app without duplicating any application logic, meaning you can add as many localizations as you care to while maintaining a single code base. Bundles are locale-specific and completely extendable so you can add new translations over time with very little effort.

If all of your application's text is requested from your back-end via gadgets.io.makeRequest calls, you can still localize your app by passing the user's country and language as request parameters and parsing them on the server. You can continue to use message bundles to localize your application's title, description, etc.

This article takes you through the steps of localizing a simple "Hello World" app using both techniques above.

Message Bundles

The first thing to do is start off with a simple application. This app includes an HTML header (static text for now) as well as a title

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
  <ModulePrefs title="Hello" description="Message bundle demo">
    <Require feature="opensocial-0.7"/>
  </ModulePrefs>
  <Content type="html">
    <![CDATA[
      <h1>Hello!</h1>
    ]]>
  </Content>
</Module>

Message bundle syntax

Next, prepare your app for localization by taking all of the text that was added above and putting it in a message bundle. A message bundle is a separate XML file that provides a list of keys and the appropriate text values for that localization. The bundle is typically named after the language and country that it's intended for: <language>_<country>.xml. In many cases, your localization will be appropriate for all users who speak a given language regardless of where they live, so you can use "ALL" in place of the country code. A message bundle for the sample above is provided here, defined in en_ALL.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<messagebundle>
  <msg name="greeting">
    Hello
  </msg>
  <msg name="title">
    My localized gadget
  </msg>
  <msg name="desc">
    Message bundle demo
  </msg>
</messagebundle>

Including Message Bundles in your gadget

Now amend your ModulePrefs to specify the location of your new message bundle as well as which language it applies to. Then change the body of your gadget slightly to display the localized greeting:

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
  <ModulePrefs title="Hello" description="Message bundle demo">
    <Require feature="opensocial-0.7"/>
    <Locale lang="en" messages="http://example.com/hello/en_ALL.xml"/>
  </ModulePrefs>
  <Content type="html">
    <![CDATA[
      <h1>__MSG_greeting__</h1>
    ]]>
  </Content>
</Module>

You can specify a 'country' attribute as well. This comes in handy when you want to localize your application for two regions that speak the same language but with subtle variations in spelling as with U.S. and U.K. English (or if you want to display a country-specific graphic like a flag). In this case, add the country attribute like so:

<Locale lang="en" country="US" messages="http://example.com/hello/en_US.xml"/>

Note that the country attribute is optional. If you have an English bundle that is appropriate for all English-speaking users, just specify the lang attribute and omit the country attribute altogether.

Now refresh the app in the sandbox (setting the bpc query string parameter to 1 to bypass the cache) to see the bundle in action. Also note that if you include neither the lang nor country attributes, the specified bundle will be used as a default if no other match is found.

Localizing ModulePrefs attributes

You can also use message bundles with ModulePrefs attributes like title and description. In fact, even if you don't use bundles anywhere else, you still need them to localize the app's title, description, and other properties.

<ModulePrefs title="__MSG_title__" description="__MSG_desc__">
  <Require feature="opensocial-0.7"/>
  <Locale messages="http://example.com/hello/ALL_ALL.xml"/>
  <Locale lang="en" messages="http://example.com/hello/en_ALL.xml"/>
  <Locale lang="pt" messages="http://example.com/hello/pt_ALL.xml"/>
</ModulePrefs>

NOTE: It is recommended that you include two Locale elements per language, one specifying just the language code and the other with a combination language-country code as in the set below:

<Locale lang="pt" messages="http://example.com/hello/pt_ALL.xml"/>
<Locale lang="pt-BR" messages="http://example.com/hello/pt_ALL.xml"/>

As a quick side note, the gadgets specification makes it easy to render text right-to-left instead of left-to-right as in English. This is outside of the scope of the this small tutorial, but you can find more information in the gadgets API documentation.

Localized text from your server

Many applications use gadgets.io.makeRequest to pull text and other data from back-end servers. Because this data is not in the application spec, message bundles aren't any help here aside from localizing an app's title, description, and other settings. Your best option in this case is to pass the user's country and language with your request. You can access these properties using the gadgets.Prefs methods getLang and getCountry. For example, the following snippet sends a request to a URL appending the language and country codes to the request URL:

<ModulePrefs title="__MSG_title__" description="__MSG_desc__">
  <Require feature="opensocial-0.7"/>
  <Locale lang="en" messages="http://example.com/hello/en_ALL.xml"/>
  <Locale lang="pt" messages="http://example.com/hello/pt_ALL.xml"/>
</ModulePrefs>
<Content type="html">
  <![CDATA[
    <script type="application/javascript">
      function sendRequest() {
        var prefs = new gadgets.Prefs();
        var urlParams = ['ctry=', prefs.getCountry(), '&lang=', prefs.getLang()];
 
        var url = 'http://example.com/hello/getGreeting.php?' + urlParams.join('');
  
        gadgets.io.makeRequest(url, callback);
      };
    </script>
    ...
  ]]>
</Content>

The passed query string arguments can then be used within the back-end script to return the proper output:

<?php
  $langCode = $_GET['lang'];
 
  header('Content-type: text/html; charset=utf-8');
 
  switch($langCode) {
    case 'en': {
      echo 'Hello!'
      break;
    }
    case 'pt': {
      echo 'Olá!';
      break;
    }
    case default: {
      echo 'Hello!';
      break;
    }
  }
 
?>

Notice that message bundles are included in this example as well. This way, your app's title, description, and other properties can be localized along with its content, which your server provides.