The system is localized for the languages and locales offered as options in the Language picker in any given release of Palm webOS. You can localize your application to any of the available locales and the correct localization will be selected when the application is launched. If your application doesn’t support the selected locale, it will try to match just the language. If there is no match for the language, the base version will be selected.
To localize your application, create locale-named directories within your application to hold the localized files. The framework will look for a directory that matches the current locale and will automatically substitute the localized content for the content in the base version.
You can localize any of the following:
Each locale can have a separate version of the application’s appinfo.json, which can be modified for that locale to customize any of the properties defined there.
Any string appearing in JavaScript executed by the application
can be extracted and localized by encapsulating the string with the
$L()
function.
Each locale has a views directory, which is structured like the base version views directory and can contain any of the base version HTML files that you wish to modify for that locale.
Any string appearing in an HTML file or template can be localized by creating a copy of the file and modifying the strings.
Typically, you will develop your application with a base version and once you reach UI freeze or the UI is complete, you will do a test localization with a pseudolocale to confirm that you have identified all the strings and that your application structure is correct.
You can localize to multiple languages in parallel, but once you’ve done your localization, you will need to manage code or UI changes. This may be difficult under the current structure, as the tools don’t address change management, forcing you to manually migrate all changes.
It’s good to do a pseudolanguage localization early to test localization readiness, but wait on actual localization until your UI is final or very close to it.
Localized applications have an additional directory, resources, at the application’s root directory, and within it are directories for each locale. Within resources are locale directories that include a localized version of appinfo.json and localization files for JavaScript string translation (strings.json), and localized versions of the application’s view files. Figure 11-2 shows an example of an exploded resources directory for the News application, which has a single localization for United States Spanish.
You’ll see that the first level of directories within resources is for locales. If the locale is set to es_US, the framework will look for content in the resources/es_us directory first. If that directory or the content (e.g., appinfo.json, specific strings in strings.json, HTML files in /views) is not there, it will then default to the app directory contents.
You can create this structure manually by creating and naming the directories with the valid language and region codes. Table 11-1 defines the initially supported languages and regions, but check the SDK documentation for the current set of supported names. All of the language names are encoded as a two-letter ISO 639 language code and all of the region names are based on a two-letter ISO 3166 country code.
At the time of this writing, there are no tools provided in the SDK to assist with creating or maintaining localization content, but tools are planned. Check the SDK site for any new information on localization tools.
Each locale directory will include a copy of the main appinfo.json file, modified for localization and for file path changes. For the News application, the title is changed to “Noticias” and the main and icon property values are modified to adjust the relative path for the location of this version of appinfo.json. The file path must reflect that this version of appinfo.json is located at com.palm.app.news/resources/es_us/appinfo.json and the target files are located at com.palm.app.news:
{ "title": "Noticias", "type": "web", "main": "../../index.html", "icon": "../../icon.png", "id": "com.palm.app.news", "version": "1.0", "vendor": "Palm", "theme": "light", "noWindow": "true" }
We only localized the application name here, but you could also do the following:
Change the icon by pointing to a different icon image file. The appinfo.json file is encoded in UTF-8.
Use a localized index.html, which might be useful for including different stylesheets for a given localization or for changing page layout based on writing direction or cultural reasons.
The Mojo framework will dynamically substitute localized strings based on the current locale. Any JavaScript string can be localized; you should localize all text strings that are displayed and are visible to the user, but not those that never appear. Examples of strings that should not be localized include logging information or information used strictly as internal data or arguments. Nor should you localize strings that don’t include text, such as HTML templates that contain only variable definitions.
You prepare for string extraction and translation by
encapsulating any target string with the $L()
function, as in
this example from app-assistant.js in News, where the default
application menu attributes and model are set up:
// Setup App Menu for all scenes; all menu actions handled in // AppAssistant.handleCommand() News.MenuAttr = {omitDefaultItems: true}; News.MenuModel = { visible: true, items: [ {label: $L("About News..."), command: "do-aboutNews"}, Mojo.Menu.editItem, {label: $L("Update All Feeds"), checkEnabled: true, command: "do-feedUpdate"}, {label: $L("Preferences..."), command: "do-newsPrefs"}, Mojo.Menu.helpItem ] };
Or in this case, from preferences-assistant.js, where the list selector widget is set up:
// Setup list selector for UPDATE INTERVAL this.controller.setupWidget("feedCheckIntervalList", { label: $L("Interval"), choices: [ {label: $L("Manual Updates"), value: "00:00:00"}, {label: $L("5 Minutes"), value: "00:05:00"}, {label: $L("15 Minutes"), value: "00:15:00"}, {label: $L("1 Hour"), value: "01:00:00"}, {label: $L("4 Hours"), value: "04:00:00"}, {label: $L("1 Day"), value: "23:59:59"} ] }, this.feedIntervalModel = { value : News.feedUpdateInterval });
The framework will use any string that is encapsulated with
$L()
as a key in the appropriate
strings.json
file (based on the
current locale) and substitute the resulting value (the localized
string) in the original string’s place.
In this next example, the string arguments to Mojo.Log.warn()
are not encapsulated, but
the arguments to Mojo.Controller.errorDialog()
are:
Mojo.Log.warn("Can't open feed database: ", result); Mojo.Controller.errorDialog($L("Can't open feed database : ")+ result);
Sometimes translations can change the position of variables within the strings. If a variable is inserted within a string, use templates to allow for changes in position. For example:
Mojo.Log.warn("Can't open feed database (#", result, "). All feeds will be reloaded."); // ** These next two lines are wrapped for book formatting only ** var message = new Template($L("Can't open database (#{message}). Feeds will be reloaded.")); Mojo.Controller.errorDialog(message.evaluate({message: result}));
This is even more critical if you have strings where there are multiple variables. Localization can change not only the location, but also the order of variables within strings. For example:
"Not enough memory to #{action} the file #{fname}."
becomes (in Finnish):
"Liian vähän muistia tiedoston #{fname} #{action}."
Also, be careful about reusing keys where the same original string might have multiple translations in another language. For example, “save” can be translated in one context to “speichern” in German. In another, it could be translated as “retten.” Likewise, the English word “add” can mean “append” in one context, and “sum” in another context. When using keys in different contexts, consider using different identifiers in your source documents, even if they both have the same localization into English.
Once the strings are identified, you will extract them to a strings.json file, located in the root level of the locale directory.
You can manually extract the strings by scanning the file for
$L()
encapsulation and copying the
strings into the key position in strings.json. If they aren’t encapsulated
by the $L()
function, the framework
will not perform the substitutions.
Palm recognizes that this is a very tedious process and intends to provide tool support to facilitate string extraction, but at the time of this writing, those tools are not yet available. Refer to the Palm developer site for more information about localization tools.
The strings.json file is a conventional JSON file, encoded in UTF-8, with the base version of the string used as a key and the localized version of the string as the value:
"Original String" : "Localized String",
The News resources/es/strings.json is shown here:
{ "#{status}" : "#{status}", // ** These next four lines are wrapped for book formatting only ** "0##{title} : No New Items|1##{title} : 1 New Item|1>##{title} : #{count} New Items" : "0##{title} : No hay elementos nuevos|1##{title} : 1 elemento nuevo|1>##{title} : #{count} elementos nuevos", "1 Day" : "1 día", "1 Hour" : "1 hora", "15 Minutes" : "15 minutos", "4 Hours" : "4 horas", "5 Minutes" : "5 minutos", "About News..." : "Acerca de noticias...", // ** These next three lines are wrapped for book formatting only ** "Add Feed DB save error : #{message}; can't save feed list." : "Error de base de datos al intentar agregar nueva fuente web : #{message}; no se puede guardar la lista de fuentes web.", "Add News Feed Source" : "Añadir fuente web de noticias", "Add..." : "Añadir...", "Adding a Feed" : "Añadiendo una fuente web", "All Read" : "Todas leídas", "All Unread" : "Todas las no leídas", "Can't open feed database: " : "No se puede abrir la base de datos de fuentes web: ", "Cancel" : "Cancelar", "Cancel search" : "Cancelar búsqueda", "Check out this News story..." : "Leer esta noticia...", "Check this out: " : "Mira esto: ", "Copyright 2009, Palm Inc." : "Copyright 2009, Palm Inc.", "Database save error: " : "Error al guardar en la base de datos: ", "Edit a Feed" : "Editar una fuente web", "Edit Feed" : "Editar fuente web", "Edit News Feed" : "Editar una fuente web de noticias", "Feature Feed" : "Fuente web destacada", "Featured Feed" : "Fuente web destacada", "Feature Rotation" : "Rotación de fuente web destacada", "Feed Request Success:" : "Solicitud de fuente web lograda:", "Feed Updates" : "Actualización de fuentes web", "Help..." : "Ayuda...", "Interval" : "Intervalo", // ** These next two lines are wrapped for book formatting only ** "Invalid Feed - not a supported feed type" : "Fuente web no válida: no es un tipo de fuente web admitido", "Latest News" : "Últimas noticias", "Manual Updates" : "Actualizaciones manuales", "Mark Read or Unread" : "Marcar leída o no leída", "New Card" : "Tarjeta nueva", "New features" : "Nuevas características", "New Items" : "Elementos nuevos", "News Help" : "Ayuda para noticias", "News Preferences" : "Preferencias para noticias", // ** These next two lines are wrapped for book formatting only ** "newsfeed.status" : "Estado #{status} devuelto desde solicitud de fuente web de noticias", "OK" : "OK", "Optional" : "Opcional", "Preferences..." : "Preferencias...", "Reload" : "Cargar nuevamente", "Rotate Every" : "Girar cada", "Rotation (in seconds)" : "Rotación (en segundos)", "RSS or ATOM feed URL" : "Fuente web RSS o ATOM URL", "Search for: #{filter}" : "Buscar: #{filter}", "Show Notification" :"Mostrar aviso", "SMS/IM" : "SMS/IM", // ** These next two lines are wrapped for book formatting only ** "Status #{status} returned from newsfeed request." : "La solicitud de fuente web de noticias indicó el estado #{status}.", "Stop" : "Detener", "Title" : "Título", "Title (Optional)" : "Título (Opcional)", "Update All Feeds" : "Actualizar todas las fuentes web", "Wake Device" : "Activar dispositivo", // ** These next two lines are wrapped for book formatting only ** "Will need to reload on next use." : "Se tendrá que cargar de nuevo la próxima vez que se use." }
If the original string is not appropriate as a key, the $L()
function can be called with an explicit
key:
$L("value":"Original String", "key": "string_key")
In this case, string_key
is
the key in strings.json and the
translation of Original String
is
the value:
{ "string_key" : "Localized String" }
Here’s an example, again in News. The results template is
defined with a key inside the $L()
function:
AppAssistant.prototype.feedRequestSuccess = function(transport) { var t = new Template($L({key: "newsfeed.status", value: "Status #{status} returned from newsfeed request."})); Mojo.Log.info("com.palm.app.news - Feed Request Success: ", t.evaluate(transport));
And if you look back at the strings.json, you’ll see this entry, where the key is used instead of the original string:
// ** These next two lines are wrapped for book formatting only ** "newsfeed.status" : "Estado #{status} devuelto desde solicitud de fuente web de noticias",
The framework does not dynamically substitute localized text strings for HTML text strings. Instead you will create a copy of the HTML scenes and templates, manually substituting the localized strings. You do get some help identifying strings and extracting them to strings.json to facilitate translation, but you’ll need to manually copy the translated strings to your localized HTML files.
You don’t have to limit the changes in the localized HTML files to localized text substitution. You may also make layout changes by locale; in some cases the translations may need some adjustments. And you can also include locale-specific CSS, which you can use in combination with your HTML to modify the presentation, either to accommodate text translations (for example, adjusting for significant changes in text length) or simply to address unique formatting requirements within a specific locale.
You don’t need to extract the strings from the HTML files, but instead you will copy any HTML file that includes a localized string into the views directory for each locale. The structure of the views folder will mirror the base version, but only the files with localized content will be included. For example, News will have only three localized HTML files:
views/feedList/addFeed-scene.html |
views/feedList/feedList-assistant.html |
views/preferences/preferences-assistant.html |
As part of your localization workflow, you may want to extract the strings, and it doesn’t hurt to include those strings in strings.json.
Localizers are very familiar with translating whole HTML files. You may want to just hand over your file to the localizers to get translated to various languages and not take time to extract the strings.
After translation, the localized strings must be copied into the localized version of the HTML file. Using the previous example of preferences-assistant.html, the strings are translated to es_US:
<div class="palm-page-header"> <div class="palm-page-header-wrapper"> <div class="icon news-mini-icon"></div> <div class="title">Preferencias para noticias</div> </div> </div> <div class="palm-group"> <div class="palm-group-title"><span>Fuente web destacada</span></div> <div class="palm-list"> <div x-mojo-element="IntegerPicker" id="featureFeedDelay"></div> </div> </div> </div> <div class="palm-group"> <div class="palm-group-title"><span>Actualización de fuentes web</span></div> <div class="palm-list"> <div class="palm-row first"> <div class="palm-row-wrapper"> <div x-mojo-element="ListSelector" id="feedCheckIntervalList"> </div> </div> </div> <div class="palm-row"> <div class="palm-row-wrapper"> <div x-mojo-element="ToggleButton" id="notificationToggle"> </div> <div class="title left">Mostrar aviso</div> </div> </div> <div class="palm-row last"> <div class="palm-row-wrapper"> <div x-mojo-element="ToggleButton" id="bgUpdateToggle"></div> <div class="title left">Activar dispositivo</div> </div> </div> </div> </div> </div>
You can see the other HTML files along with all the localization changes made for News to support the es_US locale. When you launch News with that locale selected, you’ll see something similar to Figure 11-3.
3.131.38.208