Getting started with app localization

In this article, we will discuss how to localize Firefox OS apps — both internal Gaia apps, and third party apps. This article provides a detailed guide, but can be considered a good starting point for those new to Firefox OS localization.

Currently Firefox OS Gaia uses a modified version of the L10n.js library to localize the default apps that are available in Firefox OS. L10n.js is available in the Gaia source tree. The library relies on a key/value-based properties format.

There are many other frameworks and technologies to pick from when it comes to localization. For example, the Jed Gettext-style library is a popular traditional option.  There are also new localization platforms in development that extend the capabilities available in current libraries. For example, at Mozilla we have L20n, a very promising localization project that extends L10n with a variety of compelling new features. Gaia will be moving to L20n fairly soon, but this documentation will still be useful for legacy Firefox OS installs, and other projects.

Note: If you do decide to use the L10n approach, bear in mind that some features of the library are not cross-browser compatible (for example l10n_date.js uses the non-standard toLocaleFormat() method.)

The localization procedure

We'll localize the strings that appear in the app's HTML as well as the name and description in the app manifest. The process will take five steps:

  • Create translations: Create a file for each language we need to support that contains the translated strings.
  • Mark up the HTML: Add a new attribute to each string in the HTML that will need localization.
  • Include l10n.js: Include the l10n.js JavaScript library in the app, which inserts the correct strings into the HTML depending on locale.
  • Update manifest.webapp: Add the translations for "name" and "description" to the app manifest.
  • Link resources: Make sure the translation files can be found, and the app manifest is linked.

Create translations

First, we need to create a new directory in your app's root named locales, and a directory under there for each language you need to support. Then in each of those directories, we create a file with an extension of .properties. As an example of how it works examine one of our Gaia apps. The Bluetooth App might look like this with some translations added:

This App contains .properties files for four locales (ar, en-US, fr, and zh-TW). A portion of the file is listed below:

bluetooth = Bluetooth
confirmation = Confirmation
bluetooth-status-description = Bluetooth is disabled
turn-bluetooth-on = Do you want to turn Bluetooth on?
cancel = Cancel
turn-on = Turn On

As you can see, it’s a simple key/value property file containing the set of strings to localize. Each line is the translation of a single string in name=value format:

  • name is an identifier for this string, matching the value for a data-l10n-id attribute in the app's HTML.
  • value is the translation of the string in this particular language.

The other .properties files will contain the same string names, but the values will be translated into the particular language for each file.

Usually, the app developer won't create the contents of a translator will create it, given the set of string identifiers and their value in the original language. For more on this, see the article on working with Transifex.

Mark up the HTML

You'll need to add a new attribute to every element in your HTML whose content needs to be translated. The name of the attribute is data-l10n-id, and its value is an identifier for the string. This identifier is used as a key to look up the correct translation of the string. Here's an example:

<h2 data-l10n-id="label1">Label One</h2>

When all your attributes have been set, on page load the l10n.js library will:

  • Find all elements which have the data-l10n-id attribute set.
  • Use the attribute's value as a key to look up the correct translation of the string, given the current device language.
  • Assign this translation as the element's textContent.

There is also an attribute called data-l10n-args, which can be used to hold values like the default value for an argument. You'll learn about this later on, in Argument substitution.

The value of the label1 attribute serves as the key into the properties file. Complex strings can be created in the properties files using argument substitution and the Plural macro.

The binding between each l10n-id and its associated HTML Element is a one-to-one model. Entities should not be reused because although they may have the same value in your source language, different contexts will often require different translations.

Localizable elements should also not have any content or child elements because the localization will overwrite them.

Argument substitution

Sometimes your app needs to insert a string that should not be localized into a string that should be localized. For example, if you want to greet the user, their name should not be localized but the greeting itself should, for example "Hello, Bob!".

Argument substitution is achieved by surrounding the argument with double curly braces: {{arg}}. A message can then be customized for a specific user using syntax similar to the following:

label1 = Hello {{ user }}, glad you decided to visit

Default values for arguments can be set using the data-l10n-args attribute. This attribute expects a JSON-formatted value. In the above example, a default value could be set for the user argument using the HTML below:

<h2 data-l10n-id="label1" data-l10n-args='{ "user": "Guest" }'></h2>

This will result in the element's content being set as "Hello Guest, glad you decided to visit".

You can also reference other entities in the same way:

brandShortName = Firefox OS
helloWorld = Welcome to {{ brandShortName }}

Plural macro

Most languages use different forms of words depending on how many of a thing there are. Some languages, like English, have just two forms: singular, for "one", and plural, for "everything else, including zero":

You have no tomatoes :(.
You have one tomato.
You have five tomatoes.

Other languages have more than two forms, and some have just one.

When your app is constructing strings dynamically and their forms depend on how many of a thing there are, the localization system needs a way to represent these rules, and the app needs a way to pass the number to the localization system. For this, we have the Plural macro.

L10n.js currently supports very limited macro functionality and multi-variant strings. The plural macro can be used to customize messages based on an argument value. The macro takes a number value and returns zero, one, two, few, many, or other. The return value depends on the value passed in and the current locale’s Unicode Plural Rules. As an example, a customized mail message for the en-US locale may look something like this:

mailMessage = {[ plural(n) ]}
mailMessage[zero]  = you have no messages
mailMessage[one]   = you have one message
mailMessage[two]   = you have two messages
mailMessage[other] = you have {{ n }} messages
<h3 data-l10n-id="mailMessage" data-l10n-args='{ "n": "6" }'></h3>

This will result in the element's content being set as "You have 6 messages".

Note: You can also resolve plurals using JavaScript.

Include l10n.js

To find nodes in the HTML marked with data-l10n-id and replace their contents with values from, we need to include l10n.js alongside the other JavaScript files in your app (including it in the <head> is recommended):

<script src="js/l10n.js"></script>

Update manifest.webapp

To localize the app's name and description, we need to update the manifest.webapp file. You need to add two new fields:

  • default_locale: this is assigned a language code which will be the default. When the device is set to this language or any language not explicitly supported by the app, the name and description already specified in the manifest will be used
  • locales: this contains one property for each supported language apart from the default, listing the values to be used for name and description in that language

In this case we want to make English (en) the default, and support the other languages as well, so we would add the following lines to manifest.webapp, adding translations into the appropriate places:

"default_locale": "en",
"locales": {
  "ar": {
    "name" : " ... ",
    "description" : " ... "
 "fr": {
    "name" : " ... ",
    "description" : " ... "
 "zh-TW": {
    "name" : " ... ",
    "description" : " ... "
If you work with a translator using the Transifex service, you'll probably keep the translations of name and description in separate files called, stored alongside your other .properties files.

In order for the resources to be used to localize your HTML file, you need to link them from your HTML <head> element, like this:

<link rel="localization" href="locales/bluetooth.{locale}.properties" />

L10n.js uses the URL Template scheme, where the {locale} portion of the path will be replaced with a locale code selected as the result of language negotiation.

On top of that, you need to link to your manifest file as follows:

<link rel="manifest" href="./manifest.webapp" />

Alternatively, you can list available locales and the default locale within the HTML file using <meta> elements:

<meta name="locales" content="en-US, fr, de">
<meta name="default_locale" content="en-US">

L10n.js will do language negotiation between user-selected languages (see NavigatorLanguage.languages) and the list of available languages provided in the manifest, select the language fallback chain and loading the resources for the most preferred locale.

Testing it out/How to view different locales

Finally, test that the localization worked. Using a real Firefox OS device or the Simulator, change the device language to Spanish using one of these options:

To view your app with a different locale, change the language in Firefox or Firefox OS:

All platforms
Set the desired locale to test in JavaScript, e.g: document.webL10n.setLanguage("es");
Firefox OS (or Firefox OS Simulator via WebIDE)
Language setting in the Settings app
Choose language under Preferences > Content > Languages. More information in Set content language in Firefox

In WebIDE, Firefox, or your Firefox OS phone, you should then be able to install or update your app, and open it — The strings it contains should now appear in the language you chose, provided of course there is a translation available for that language! If not, you'll just get the English versions.

Note that because the strings in the app are replaced on page load, they're not dynamically updated when you change the language. You have to restart the app for the language change to be reflected in the app's interface.

See also

Document Tags and Contributors

 Contributors to this page: chrisdavidmills, wbamberg, dalex, jryans, robertnyman
 Last updated by: chrisdavidmills,