Magnolia 5.4 reached end of life on November 15, 2018. This branch is no longer supported, see End-of-life policy.
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 and localization, usually abbreviated as i18n and l10n respectively, are ways to adapt Magnolia to different languages and regional differences.
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.
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.
.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 | In Magnolia Maven module | In Magnolia light module |
---|---|---|
Preferred | src/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.
*) If you have modules with message bundles located within the deprecated folder, make sure that the bundles' file names are unique!
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
Each .properties
file usually contains one or more key-value pairs that provide the translations for labels, descriptions and other elements:
<key>=<translated value>
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.
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..
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.
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
DefaultMessageBundlesLoader
,
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-5652Getting 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-5909Getting issue details... STATUS
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.
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:
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.
TranslationServiceImpl
to DEBUG.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 consoleegrep -o "products(\.\w+)+"
matches message keys that start with your app name. Replace products
with your app name.sort –unique
ignores duplicate entriesproducts.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 ...
When you create UI elements in code, use the 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.
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:
messages-app.app.button.sendMessage=Send message
Examples:
Similar to Java code, you can use the translation service in a template script.
${i18n['link.readon']}
<fmt:message key="link.readon"/>
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 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:
Locale | Message files |
---|---|
English | app-contacts-messages_en.properties |
Spanish | app-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