If you’re writing a Liferay Application, you’re probably a genius who is also really cool. Which means your application is going to be used throughout the entire world. At least, it will if the messages that appear to its users can be translated into their language. Thankfully, Liferay makes it easy to support translation of your application’s language keys.

You just need to create a default language properties file (Language.properties) and one for each translation you’d like to support (for example, Language_fr.properties for your French translation), and put them in the correct location in your application. Use the two letter locale that corresponds to the language you want to translate in your file names (for example, Language_es.properties provides a Spanish translation for each key).

Application localization topics:

What are Language Keys?

Each language property file holds key/value pairs. The key is the same in all the language property files, while the value is translated in each file. You specify the key in your user interface code, and the appropriately translated message is returned automatically for your users, depending on the locale being used in Liferay. If you have Liferay running locally, append the URL with a supported locale to see how Liferay’s language keys are translated (for example, enter localhost:8080/es).


Figure 1: Append the locale to your running Liferay’s URL and see Liferay’s translation power in action.

Language keys are just keys you’ll use in place of a hard coded, fully translated String value in your user interface code. For example, you can use a language key in your JSP via a <liferay-ui:message /> tag.

The tag might be set up like this if you’re not considering the need to translate your application’s messages:

<liferay-ui:message key="Howdy, Partner!" />

In that case you’ll get a properly capitalized and punctuated message in your application.

Alternatively, you can specify a simple key instead of the final value:

<liferay-ui:message key="howdy-partner" />

That way you can provide a translation of the key in a default language properties file (Language.properties):

howdy-partner=Howdy, Partner!

You’ll get the same output in your application with either method above, but you have the flexibility to add additional language properties files that provide translations for your application’s keys if you use the language properties approach. Use a key in your UI code, then provide the value (or translation) in your language properties file. You just need to make sure there’s a locale that corresponds to your translation.

The values from your default Language.properties file will appear if no locale is specified. If a locale is specified, Liferay will try to find a file that corresponds to the locale. For example, if a Spanish translation is sought, a Language_es.properties file must be present to provide the proper values. If it isn’t, the default language properties (from the Language.properties file) will be used.

What Locales are Available By Default?

There are a bunch of locales available by default in Liferay. Look in the portal.properties file file to find them.


To provide a translation for one of these locales, specify the locale in the file name where the translated keys will be (for example, Langauge_es.properties holds the Spanish translation).

Where do I Put Language Files?

In an application with only one module that holds all your application’s views (for example, all its JSPs) and portlet components, just create a src/main/resources/content folder in that module, and place your Language.properties and Language_xx.properties files there.

After that, make sure any portlet components (the @Component annotation in your -Portlet classes) in the module include this property:


Providing translated language properties files and specifying the javax.portlet.resource-bundle property in your portlet component is all you need to do to have your language keys translated. Then, when the locale is changed in Liferay Portal, your application’s language keys will be automatically translated.

In a more complicated, well-modularized application, you might have language keys spread over multiple modules providing portlet components and JSP files. Moreover, there might be a fair number of duplicated language keys between the modules. Thankfully you don’t need to maintain language properties files in each module.

Creating a Language Module

If you’re crazy about modularity (and you should be), you might have an application with multiple modules that provide the view layer. These modules are often called web modules.


Each of these modules can have language keys and translations to maintain, and there will probably be duplicate keys. You don’t want to end up with different values for the same key, and you don’t want to maintain language keys in multiple places. In this case, you need to go even crazier with modularity and create a new module, which we’ll call a language module.

In the root project folder (the one that holds your service, API, and web modules), create a new module to hold your app’s language keys. For example, here’s the folder structure of a language module called my-application-lang.


In the language module, create a src/main/resources/content folder. Put your language properties files here. A Language.properties file might look like this:

application=My Application
add-entity=Add Entity

Create any translations you want, adding the translation locale ID to the language file name. File Language_es.properties might look like this:

my-app-title=Mi Aplicación
add-entity=Añadir Entity

On building the language module, Liferay Portal’s ResourceBundleLoaderAnalyzerPlugin detects the content/Language.properties file and adds a resource bundle capability to the module. A capability is a contract a module declares to Liferay Portal’s OSGi framework. Capabilities let you associate services with modules that provide them. In this case, Liferay Portal registers a ResourceBundleLoader service for the resource bundle capability.

Next, you’ll configure a web module to use the language module resource bundle.

Using a Language Module

A module or traditional Liferay plugin can use a resource bundle from another module and optionally include its own resource bundle. OSGi manifest headers Require-Capability and Provide-Capability make this possible, and it’s especially easy in modules generated from Liferay project templates. Instructions for using a language module are divided into these environments:

If you’re using bnd with Maven or Gradle, you need only specify Liferay’s -liferay-aggregate-resource-bundle: bnd instruction–at build time, Liferay’s bnd plugin converts the instruction to Require-Capability and Provide-Capability parameters automatically. Both approaches are demonstrated.

Using a Language Module from a Module

Modules generated from Liferay project templates have a Liferay bnd build time instruction called -liferay-aggregate-resource-bundles. It lets you use other resource bundles (e.g., including their language keys) along with your own. Here’s how to do it:

  1. Open your module’s bnd.bnd file.

  2. Add the -liferay-aggregate-resource-bundles: bnd instruction and assign it the bundle symbolic names of modules whose resource bundles to aggregate with the current module’s resource bundle.

    -liferay-aggregate-resource-bundles: \

For example, a module that uses resource bundles from modules com.liferay.docs.l10n.myapp1.lang and com.liferay.docs.l10n.myapp2.lang would set this in its bnd.bnd file:

-liferay-aggregate-resource-bundles: \

The current module’s resource bundle is prioritized over those of the listed modules.

At build time, Liferay’s bnd plugin converts the bnd instruction to Require-Capability and Provide-Capability parameters automatically. In traditional Liferay plugins, you must specify the parameters manually.

Using a Language Module from a Traditional Plugin

To use a language module, from a traditional Liferay plugin you must specify the language module using Require-Capability and Provide-Capability OSGi manifest headers in the plugin’s liferay-plugin-package.properties file.

Follow these steps to configure your traditional plugin to use a language module:

  1. Open the plugin’s liferay-plugin-package.properties file and add a Require-Capability header that filters on the language module’s resource bundle capability. For example, if the language module’s symbolic name is myapp.lang, you’d specify the requirement like this:

    Require-Capability: liferay.resource.bundle;filter:="(bundle.symbolic.name=myapp.lang)"
  2. In the same liferay-plugin-package.properties file, add a Provide-Capability header that adds the language module’s resource bundle as this plugin’s (the myapp.web plugin) own resource bundle:


In this case, the myapp.web plugin solely uses the language module’s resource bundle—the resource bundle aggregate only includes language module myapp.lang.

Aggregating resource bundles comes into play when you want to use your a language module’s resource bundle in addition to your plugin’s resource bundle. These instructions show you how to do this, while prioritizing your current plugin’s resource bundle over the language module resource bundle. In this way, the language module’s language keys compliment your plugin’s language keys.

For example, a portlet whose bundle symbolic name is myapp.web uses keys from language module myapp.lang, in addition to its own. The portlet’s Provide-Capability and Web-ContextPath OSGi headers accomplish this.


Let’s examine the example Provide-Capability header.

  1. liferay.resource.bundle;resource.bundle.base.name="content.Language" declares that the module provides a resource bundle whose base name is content.language.

  2. The liferay.resource.bundle;resource.bundle.aggregate:String=... directive specifies the list of bundles whose resource bundles are aggregated, the target bundle, the target bundle’s resource bundle name, and this service’s ranking:

    • "(bundle.symbolic.name=myapp.web),(bundle.symbolic.name=myapp.lang)": The service aggregates resource bundles from bundles bundle.symbolic.name=myapp.web (the current module) and bundle.symbolic.name=myapp.lang. Aggregate as many bundles as desired. Listed bundles are prioritized in descending order.
    • bundle.symbolic.name=myapp.web;resource.bundle.base.name="content.Language": Override the myapp.web bundle’s resource bundle named content.Language.
    • service.ranking:Long="4": The resource bundle’s service ranking is 4. The OSGi framework applies this service if it outranks all other resource bundle services that target myapp.web’s content.Language resource bundle.
    • servlet.context.name=myapp-web: The target resource bundle is in servlet context myapp-web.

Now the language keys from the aggregated resource bundles compliment your plugin’s language keys.

Did you know that Liferay Portal’s core language keys are also available to your module? They’re up next.

Using Liferay Portal’s Language Properties

If you have Liferay Portal’s source code, you can check out Liferay Portal’s core language properties by looking in the portal-impl/src/main/content folder. Otherwise, you can look in the portal-impl.jar that’s in your Liferay bundle.


[Liferay Home]/tomcat-[version]/webapps/ROOT/WEB-INF/lib/portal-impl.jar

These keys are available at runtime, so when you use any of Liferay Portal’s default keys in your user interface code, they’re automagically swapped out for the appropriately translated value. Using Liferay Portal’s keys where possible saves you time and ensures that your application follows Liferay’s UI conventions.

If you want to generate language files for each supported locale automatically, or to configure your application to generate translations automatically using the Microsoft Translator API, check out the tutorial Automatically Generating Language Files.

0 (0 Votes)
Internationalization Previous