A note from 2026: This article was published in 2016. SAP Hybris Commerce is now SAP Commerce Cloud, and accelerator storefront templates and add-ons are legacy in many projects; validate this approach against your current SAP Commerce Cloud version and deployment model before using it.

Introduction

There is a well-known mechanism for internationalization (i18n) in Java known as resource bundles. They contain locale-specific resources, usually one file per language version. Hybris uses the following naming convention for the files used as resource bundles:

Resource bundle file naming convention

There are different families of bundles for different websites and add-ons. Each resource bundle in the family contains the same items, but the items are translated for the locale represented by that resource bundle. These are key/value pairs. The keys uniquely identify a locale-specific object in the bundle.

Hybris uses this concept for system messages, labels, and button titles used in templates to make them dependent on a particular website state, such as the language version.

Bundle properties are named after the content they are supposed to describe. There is a convention to use dot-separated words. For example,

Example of dot-separated bundle property keys

There are more than 2,000 items used mainly in templates. Some of the items are used directly from Java code. Many of them are not used at all.

Example of resource bundle usage in Java code

Using CMS, you can change page components and their parameters, but these messages are not editable. If you need to change them, you need to ask the developers because these files are part of the codebase. It can be said that these messages are hard-coded. It is good that all of them are in the same place and tagged, but the truth is that they are not editable by administrators.

If you find a shameful typo when your website is up and running, it can take hours to fix it. From a technical perspective, there is not a big difference between fixing a minor bug in the code and fixing a typo in the resource files. In both cases, you need to create a patch and apply it to the cluster.

That is why some of my clients ask to move the messages from the file system to the database to make them editable.

In addition, managing localized messages is very convenient for translators.

For example, some design elements may not be suitable for the translated text if it is much longer or shorter than the original version. There is a property named:

checkout.multi.paymentMethod.addPaymentDetails.generalError

Let’s compare its values for English and German:

English version:An error occurred contacting the payment provider. Wait a few minutes and try again. If the problem persists, please contact our sales team.(139 chars)

German version:Während der Kontaktaufnahme zum Zahlungsanbieter ist ein Problem aufgetreten. Warten Sie einige Minuten und versuchen Sie es erneut. Wenn das Problem weiterhin auftritt, wenden Sie sich an unser Vertriebsteam.” (209 chars)

The difference is 70 characters, which could be too much for the window or area designed for the message. It is clear that all these cases should be verified before the system goes live, but multiply the number of language versions by the number of messages in these files (~2,000), and you will get the number of items in your QA checklist.

However, suffice it to say that most of these messages are tightly connected to the visual design and functionality. For example, the property named:

general.month.january

contains “July” in the English package, and nobody wants to change it to any other value. Some messages have parameters, such as:

popup.cart.showing

with the value “Showing {0} of {1} Items”. Some messages have HTML tags. Some messages can be used in different contexts. In the article about the CMS template structure, you will find the template/property mapping that is useful for analysis.

Solution

The extension I would like to introduce pulls the messages from the database instead of the file system. It also allows administrators to manage the localized properties using the Backoffice interface. All changes are immediately applied to the storefront.

The important feature of this solution is that it is fully compatible with the existing templates.

Localization messages managed from Backoffice

Screenshots

Backoffice screen for localized messages

Technical details

HybrisResourceLoader

public class HybrisResourceLoader extends DefaultResourceLoader implements ResourceLoader {
    Map<String, Resource> resourceCache = new HashMap<>();
    @Override
    public Resource getResource(String s) {
       Resource resource = getResourceFromCache(s);
       if (resource == null) {
         Resource hybrisMessageResource = getResourceFromHybris(s);
         addResourceToCache(s, fileresource);
         ...
       }
       ...
     }
    }
    private addResourceToCache(String s, Resource resource ) { resources.add(s, resource) }
    private getResourceFromCache(String name) { return resources.get(s); }
    private getResourceFromHybris(String s) { ... }
    public clearCache (String name)
}

HybrisReloadableResourceBundleMessageSource

public class HybrisReloadableResourceBundleMessageSource extends ReloadableResourceBundleMessageSource {
    @Resource
    HybrisResourceLoader hybrisResourceLoader;
    HybrisReloadableResourceBundleMessageSource(){
        super.setResourceLoader(hybrisResourceLoader);
    }
    public void setResourceLoader(ResourceLoader resourceLoader) {
        super.setResourceLoader(hybrisResourceLoader);
    }
}

HybrisStorefrontResourceBundleSource

public class HybrisStorefrontResourceBundleSource extends StorefrontResourceBundleSource {
    @Resource
    HybrisResourceLoader hybrisResourceLoader;
    protected AbstractMessageSource createMessageSource(final String basename)
    {
        ReloadableResourceBundleMessageSource messageSource = (ReloadableResourceBundleMessageSource) super.createMessageSource(basename);
        messageSource.setResourceLoader(hybrisResourceLoader);
        return messageSource;
    }
}

HybrisResourceUpdatedEventListener

public class HybrisResourceUpdatedEventListener extends AbstractEventListener {
    static final private Logger LOG = Logger.getLogger(HybrisResourceUpdatedEventListener.class);
    @Autowired
    HybrisResourceLoader hybrisResourceLoader;
    @Override
    public void onEvent(final HybrisResourceUpdatedEvent event)
    {
        hybrisResourceLoader.clearCache(event.getName());
    }
}

© Rauf Aliev, October 2016