Magnolia 5.3 reached end of life on June 30, 2017. This branch is no longer supported, see End-of-life policy.
Take advantage of Magnolia's internationalization (i18n) mechanism to make your app accessible to users in many languages and locales. Do not use hard coded message strings in your code. Instead, move all message keys and strings to a text file that translators and localizers can work with easily.
.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.
To create a message bundle:
/src/main/resources/mgnl-i18n
in the module source files. If the folder doesn't exist, create it.app-<app-name>-messages_en.properties
. This is the first file in the message bundle.Your folder hierarchy should look like this:
Every app should have its own message bundle. So if your module deploys two apps, create two bundles.
Magnolia will find your message files as long as they are in the mgnl-i18n
folder, so the file name is not significant. However, we recommend that you follow this pattern:
app-<app-name>-messages_<locale>.properties
Examples:
app-security-messages_en.properties
app-contacts-messages_de.properties
app-products-messages_zh_CN.properties
Make sure the locale is correct. Magnolia follows the Java locale notation.
Magnolia 5.1+: Add message files in
<module>/src/main/resources/mgnl-i18n/
. The files will be loaded automatically during module startup. If your project uses message bundles from other locations there is no need to move their files – the old i18n mechanism still works. However, if you create a new message bundle on Magnolia 5.1+ we recommend you add the files in the mgnl-i18n
directory.
The old i18n mechanism is still used in templating. Templates do not use the updated i18n features. See - MAGNOLIA-5876Getting issue details... STATUS .
Add keys to your message file. Here is an example pattern. Copy the text to the .properties
file and replace the placeholders with the names your apps, subapps, actions, form fields and messages.
# App name and icon <app-name>.app.label= <app-name>.app.icon= # Action bar sections <app-name>.<subapp-name>.actionbar.sections.<section-name>.label= <app-name>.browser.actionbar.sections.root.label= <app-name>.browser.actionbar.sections.folder.label=Folder <app-name>.browser.actionbar.sections.preview.label=Preview # Actions <app-name>.<subapp-name>.actions.<action-name>.label= <app-name>.browser.actions.addFolder.label= <app-name>.browser.actions.editFolder.label= <app-name>.browser.actions.deleteFolder.label= <app-name>.detail.actions.cancel.label= <app-name>.detail.actions.save.label= # Editor <app-name>.label= <app-name>.description= # Forms <app-name>.<tab-name>.label= <app-name>.<tab-name>.<field-name>.label= <app-name>.<tab-name>.<field-name>.description= # Dialogs <app-name>.<dialog-name>.label= <app-name>.<dialog-name>.description= # Message views <app-name>.<message-view-name>.actionbar.sections.<section-name>.label= <app-name>.<message-view-name>.actions.<action-name>.label= <app-name>.<message-view-name>.actions.<action-name>.successMessage= <app-name>.<message-view-name>.actions.<action-name>.errorMessage= <app-name>.<message-view-name>.actions.<action-name>.failureMessage= # Templates <module-name>.pages.<page-name>.title= <module-name>.pages.<page-name>.description= <module-name>.areas.<area-name>.title= <module-name>.areas.<area-name>.description= <module-name>.components.<component-name>.title= <module-name>.components.<component-name>.description=
Keys in message files in the mgnl-i18n/
directory must be unique throughout all files that directory. If you follow the pattern shown above your keys will be unique.
Can't find a message key? Try logging the info.magnolia.i18nsystem.TranslationServiceImpl
class while you use your app. This class prints keys into the catalina.out
log file as Magnolia looks for them in bundles.
TranslationServiceImpl
to DEBUG.Looking up in global i18n message bundle with key
.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 ...
At startup, Magnolia creates a global message bundle by reading keys from all .properties
files in mgnl-i18n
folders of all modules in the current project. The global bundle is a virtual bundle, not a physical file. It includes keys from your module and keys from Magnolia's own modules such as ui-admincentral
and ui-framework
.
This means that you don't need to provide keys for all UI elements in your app. Keys for commonly reused elements such as buttons and subapps are in Magnolia's own modules. Just reuse the UI element in your app and Magnolia will find the existing key automatically.
Example: The contact edit form has two buttons: Save Changes and Cancel. The Contacts app doesn't provide keys for either button in its own message bundle. The keys are provided in module-ui-framework-messages_en.properties
.
# Default Action Labels actions.commit=save changes actions.cancel=cancel
If your app needs these buttons, just name your dialog actions commit
and cancel
.
Node name | Value |
---|---|
actions |
|
commit |
|
class | info.magnolia.ui.form.action.SaveFormActionDefinition |
implementationClass | info.magnolia.contacts.app.form.action.SaveContactFormAction |
cancel |
|
class | info.magnolia.ui.form.action.CancelFormActionDefinition |
Use the SimpleTranslator translation service when you create UI elements in code. It is a good practice to make all user interface text translatable. 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.
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:
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"/>
The i18n API generates keys for some objects automatically. This means you don't need to add i18nBasename, label and description properties. The new API is practical especially in dialogs which usually have a lot of translatable text.
The following logic generates automatic keys.
If an object is able to autogenerate its keys, its class or superclass or usually its interface is annotated with
I18nable
. It means, these objects are internationalized. When annotating classes with I18nable
, you must provide the responsible Keygenerator class which implements
I18nKeyGenerator
.
Internationalized classes also have methods annotated with I18nText . Every annotated method leads to an autogenerated key. Depending on the Keygenerator, the generator may generate more than one key for the same method.
Example: DialogDefinition
/** * Base definition of a dialog. */ @I18nable(keyGenerator = DialogDefinitionKeyGenerator.class) public interface DialogDefinition { String getId(); @I18nText String getLabel(); String getI18nBasename(); Map<String, ActionDefinition> getActions(); Class<? extends DialogPresenter> getPresenterClass(); EditorActionAreaDefinition getActionArea(); ModalityLevel getModalityLevel(); boolean isWide(); }
Note the annotations on lines 4 and 8.
When objects of internationalized classes are instantiated, a proxy will generate a decorated object which knows its keys and which is able to fetch the translations for these keys according to the context.