You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 3 Next »

This page explains the internationalization concept as applied in Magnolia. The first part of the page provides the internationalization basics, the second part mentions some more advanced points.

Internationalization vs localization

Internationalization and localization, usually abbreviated as i18n and l10n respectively, are ways to adapt Magnolia to different languages and regional differences. 

  • Internationalization involves designing a system in which it is easy to accommodate new languages without programming.
  • Localization adapts Magnolia to a particular region or language by adding locale-specific configuration.

i18n vs l10n

Magnolia primarily provides technical means for internationalization and it is mainly these aspects that the text below and other parts of the Language section deal with. For more information regarding l10n and the differences between i18n and l10n, see for example W3C's page.

The running system assigns a  java.util.Locale  depending on the user settings or browser information. While rendering a page, a dialog, an app or any other part or the user interface, the i18n system evaluates the proper translation for a text by the current Locale. It may happen that the i18n system cannot find a translation for the given locale - in this case the translation of the default language is applied.

The basics of Internationalization with message bundles

All types of translatable text (besides editorial content stored in JCR) are internationalized with message bundles. With message bundles a translator can work with a plain text file and doesn't need to touch the code. To do this, you need message bundles which contain message keys.

i18n message bundle

A message bundle (resource bundle in Java) is a collection of .properties files. Each file contains key-value pairs of translated user interface text such as labels and messages. The keys in all files of the same bundle are identical but the values are language specific translations.

A message bundle must contain at least one .properties file. The files are named after the language (locale): <bundle-name>_<locale>.properties, for example app-pages-messages_en.properties. Every Magnolia module should provide its own message bundle. If a module installs several apps, each app should have its own message bundle.

Location of message bundles


LocationIn Magnolia Maven moduleIn Magnolia light module
Preferredsrc/main/resources/<module-name>/i18n<magnolia.resources.dir>/<module-name>/i18n
Deprecated*src/main/resources/mgnl-i18n-

Message bundles stored in these directories will be loaded automatically during module startup.

*) (warning) If you have modules with message bundles located within the deprecated folder, make sure that the bundles' file names are unique!

Message bundle file name

Magnolia will find your message bundle files as long as they are in the i18n folder (or in the deprecated folder). Message bundle file names must have the following form:

<bundle-name>_<locale>.properties

The  <locale>  part of the filename, defining the i18n language used in the  .properties  file, must conform with the  Java locale notation.  

Bundle name is arbitrary. However, it is a good practice to use one of the following patterns:

app-<app-name>-messages_<locale>.properties
<module-name>-messages_<locale>.properties

i18n message keys

Each .properties file usually contains one or more key-value pairs that provide the translations for labels, descriptions and other elements:

<key>=<translated value>

(warning) In Magnolia documentation we often use the term key to mean a key-value pair.

The keys may be module- and/or (sub)app-specific in which case the module name and/or the (sub)app name forms a part of the key, e.g.

products.browser.actionbar.sections.root.label=Products

This key will assign the word "Products" to the label for the root section of the actionbar of the Browser subapp in the Products app.

For standard Magnolia components, such as dialogs, forms, fields etc., Magnolia i18n API generates keys automatically. You will only have to define keys for the so called template labels and for texts within custom java classes.

Referencing keys on the UI

Once you have defined the keys in the message bundle, the keys can be referenced in the UI. If applied correctly, the UI will render the values of the referenced keys in the language of the current locale settings.

*) Magnolia references i18n keys in templates, dialogs, apps, forms, fields, messages, etc..

Unicode and UTF-8 encoding

UTF-8 is the dominant character encoding for the World Wide Web. Magnolia supports UTF-8 character encoding for Unicode. UTF-8 can represent any character in the Unicode standard and is backwards compatible with ASCII.

Best practice

Magnolia requires everyone to use UTF-8 character encoding in .properties files.

The Java Content Repository (JCR) will store values in whatever format you provide. Magnolia always ensures that all values are UTF-8 encoded. Java classes read .properties files of message bundles from the file system. Depending on the implementation of these stream reading classes, Java allows you to set the encoding in which the files are read.
Magnolia's i18n framework assumes that files in message bundles are UTF-8 encoded (see 

$webResourceManager.requireResource("info.magnolia.sys.confluence.artifact-info-plugin:javadoc-resource-macro-resources") DefaultMessageBundlesLoader
$webResourceManager.requireResource("info.magnolia.sys.confluence.artifact-info-plugin:javadoc-resource-macro-resources") DefaultMessagesImpl
).

If you open some older properties files in some of the Magnolia modules you will find keys like this:

link.readon = P\u0159e\u010d\u00edst

This file contains Unicode entities or Java Unicode Character Representations. When you use Unicode entities to encode special characters, it doesn't matter what encoding your file is stored in or read by the Java class.

However, the above is a legacy practice that is no longer necessary. Since MAGNOLIA-5652 - Getting issue details... STATUS  you don't have to use Unicode entities, just make sure your properties files are UTF-8 encoded. The above snippet from the bundle with basename info.magnolia.module.templatingkit.messages can be written like this:

link.readon = Přečíst

Use ISO-8859-1 with JSP

Message bundles for JSP templates are read by standard JSTL mechanism which reads files with ISO-8859-1 encoding by default. Save your properties files with ISO-8859-1 encoding or use Unicode entities when working with JSP. If you cannot use Unicode entities, avoid using the same message bundle for JSP and Freemarker scripts. JSP can be forced to explicitly use UTF-8 encoding when reading properties files. MAGNOLIA-5909 - Getting issue details... STATUS

More advanced points

Finding the full name of a key

Magnolia generates keys for all dialog text automatically. If a translation for a key is not found in the existing .properties file(s), Magnolia will use the key itself in place of the translation. The full name of a key can be obtained in several ways.

Use the browser inspector

In the following scenario, a new dialog containing a text input form is added to the Contact Page of the Travel Demo module.  After creating the dialogue, it may look like this in the browser: 

Two labels in this dialog are provided with correct i18n texts: the Cancel and the Save Changes buttons. These translations are reused from message bundles of modules that already exist in the Magnolia framework.
The other two labels in the dialog have been replaced by the keys themselves and because the names of the keys are too long, the UI displays only their parts. The full names can be obtained via the inspector function of your web browser. To inspect a label, e.g. the one shortened just to travel-demo.compon...,  place the mouse cursor on the element, right-click and select "Inspect element". This works the same way in most web browsers. The following screenshots are from Firefox:

   

Upon selecting the Inspect Element option, the full key together with its translation (here actually the key name itself) will be revealed in the code window of your browser:

The page code reveals that the name of this key is

  travel-demo.components.txtField.TextFieldDialog.TextField.label

which itself has been used as the label (viz the red frame) for the dialog element. To assign to the label the translation text "Enter Your Text", add the following line to the English .properties files of the Travel Demo module:

travel-demo.components.txtField.TextFieldDialog.TextField.label=Enter Your Text

Deploy the file and start the server. The dialog will now look like this:

The translation "Enter Your Text" appears as a label attached to the input text field. Inspecting  the element again, the change will also be reflected in the code of the page:

Use the log

The full names of translation keys can also be obtained by logging the info.magnolia.i18nsystem.TranslationServiceImpl class while using an app. This class prints keys into the catalina.out log file as Magnolia looks for them in bundles.

  1. In the Log Levels app of the Log Tools Module, set the logging level for TranslationServiceImpl to DEBUG.
  2. Use your app. Open all subapps and dialogs.
  3. In the log, look for messages starting with "Looking up in global i18n message bundle with key..."
catalina.out raw output
2014-04-04 13:10:45,604 DEBUG info.magnolia.i18nsystem.TranslationServiceImpl   : Looking up in global i18n message bundle with key [[sample.app.label, sample.app]] and Locale [de]
2014-04-04 13:10:45,605 DEBUG info.magnolia.i18nsystem.TranslationServiceImpl   : Looking up in global i18n message bundle with key [[sample.app.icon]] and Locale [de]
2014-04-04 13:10:45,605 DEBUG info.magnolia.i18nsystem.TranslationServiceImpl   : Looking up in global i18n message bundle with key [[app-launcher.dev.label, app-launcher.dev, app-launcher.dev]] and Locale [de]
2014-04-04 13:10:45,605 DEBUG info.magnolia.i18nsystem.TranslationServiceImpl   : Looking up in global i18n message bundle with key [[products.app.icon]] and Locale [de]
2014-04-04 13:10:45,612 DEBUG info.magnolia.i18nsystem.TranslationServiceImpl   : Looking up in global i18n message bundle with key [[pages.app.label, pages.app]] and Locale [de]
...

The raw log output can be confusing since it also contains keys from other Magnolia apps. Parse the log to find keys that are relevant to your app only. Here's an example command you can run in an OS X Terminal:

cat catalina.out | egrep -o "products(\.\w+)+" | sort --unique

Where:

  • cat catalina.out prints the contents of the log file to the console
  • egrep -o "products(\.\w+)+"  matches message keys that start with your app name. Replace products with your app name.
  • sort –unique ignores duplicate entries
catalina.out parsed output
products.app
products.app.icon
products.app.label
products.browser
products.browser.actionbar.sections.folder
products.browser.actionbar.sections.folder.label
products.browser.actionbar.sections.product
products.browser.actionbar.sections.product.label
...

Using the translation service

When you create UI elements in code, use the 

$webResourceManager.requireResource("info.magnolia.sys.confluence.artifact-info-plugin:javadoc-resource-macro-resources") SimpleTranslator
 translation service. In a simple content app, you likely won't need to create UI elements in code since most can be configured. But if you do a custom app or anything more complex, this is how to use the service.

Java code

(warning) Do NOT instantiate SimpleTranslator but inject it in the constructor. 
import info.magnolia.i18nsystem.SimpleTranslator;
import com.vaadin.ui.Button;
import javax.inject.Inject;

public class MyClass {

    private final SimpleTranslator i18n;
	
	@Inject
    public MyClass(SimpleTranslator i18n){
        this.i18n = i18n;
    }

    public void someMethod(){
        // more code here ...
        Button sendMessageButton = new Button(i18n.translate("messages-app.app.button.sendMessage"));
        // more code here ...
    }
}

Then pass the key in the #translate(String key).

The key messages-app.app.button.sendMessage must be in a message file with a value:

myapp-messages_en.properties
messages-app.app.button.sendMessage=Send message

Examples:

Template script

Similar to Java code, you can use the translation service in a template script.

Freemarker
${i18n['link.readon']}
JSP
<fmt:message key="link.readon"/>

Example: i18n of the Tutorial module

This example shows a selection of English messages from the app-products-messages_en.properties message file in the Products app of the Tutorial module:

app-products-messages_en.properties
# App name and icon
products.app.label=Products
products.app.icon=icon-items

# Action bar sections
products.browser.actionbar.sections.root.label=Products
products.browser.actionbar.sections.folder.label=Folder
products.browser.actionbar.sections.product.label=Product
products.browser.actionbar.sections.preview.label=Preview
...

The Products app has message files for English and for Spanish:

LocaleMessage files
Englishapp-contacts-messages_en.properties
Spanishapp-contacts-messages_es.properties

These files together build a message bundle which is stored in the i18n directory within the module path:

src
└── main
    └── resources
        └── app-tutorial
            ├── apps
            │   └── products.yaml
            └── i18n
                ├── app-products-messages_en.properties
                └── app-products-messages_es.properties
#trackbackRdf ($trackbackUtils.getContentIdentifier($page) $page.title $trackbackUtils.getPingUrl($page))
  • No labels