A note from 2026: This article was published in 2017, when SAP Commerce Cloud was still commonly referred to as SAP Hybris Commerce. The branding, storefront architecture, and configuration conventions have changed significantly since then, especially with SAP Commerce Cloud in public cloud and SAP Composable Storefront, so validate this workaround against your current version.

Introduction

The fallback language function is designed to show content in language X if language Y is not available. It is better to show content in English rather than nothing. At least, SAP Hybris designers thought so. This mechanism is attribute-level: it works for localized attributes that do not have a localization for the current locale; additional language versions are used instead. It does not work for whole items. For example, if your product has only two fully completed language versions and three partially completed ones, you cannot redirect partially completed language versions to completed ones. It will work for each product attribute separately.

If there is no explicit localization for a certain locale, SAP Hybris Commerce relies on locales that are defined as fallback locales for that locale.

Challenges

Actually, there are two challenges:

  1. The fallback mechanism is not compatible with Java Resource Bundle. SAP admits that there is a flaw in the fallback mechanism. According to the documentation, “the fallback mechanism for de_DE is always de, [javaVMLocale] regardless what has been specified within the Hybris system.” They have an example:

    • The storefront is currently showing data for the session language es,
    • The fallback language has been set to de,
    • The Java VM has been started with en as the system locale.

    Missing localized item attributes are substituted with existing de values as intended, but missing resource bundle values are substituted by existing en values because java.util.ResourceBundle will always fall back to the system locale as a last resort.

ResourceBundle

uses a fallback mechanism from the full locale down to more general ones. For example, it first checks for:

messages_de_DE.properties

then:

messages_de.properties

down to:

messages.properties

in the end, which is the overall default.

For some configurations, the fallback mechanism will not work at all. You will have a key on your webpage instead of the actual value. For example, if you add a new key and do not create a proper record for this key in the property files, the system will show the key itself instead of an empty value.

  1. Hybris does not support separate fallback rules for different countries/websites. For example:

    • You have a multi-website setup,
    • Each website has two language versions:
      • Its own default language, native to the country, and
      • English,
    • There is a message that is not defined for the English version of the website, and
    • You want to show a message in the default language of website X if it happens.

Fallback language configuration diagram

Solution

In order to support country-dependent language fallback, the solution is pretty simple: you need to use a complex language ID that contains a country tag:

Language IDs with country tags for fallback setup

It solves the second challenging issue. However, if you need to group languages across countries and process them as a whole, you need to extend a language object by adding “language”. It can be a bit confusing, because the Language item is not a language in this solution; it is closer to a locale, but we cannot rename it.

As for the first challenge, you need to do some coding to overcome the problem. Fortunately, Hybris uses its own implementation of Resource Bundle Message Source, and using Spring, we can extend it.

spring-mvc-config.xml:

<bean id="baseMessageSource" class="org.training.core.MyReloadableResourceBundleMessageSource">
    <property name="basenames">
        <list>
            <value>/WEB-INF/messages/base</value>
        </list>
    </property>
    <property name="defaultEncoding" value="UTF-8"/>
    <property name="cacheSeconds" value="#{configurationService.configuration.getProperty('storefront.resourceBundle.cacheSeconds')}"/>
    <property name="fallbackToSystemLocale" value="false"/>
</bean>

In the following code sample, I decided to kill two birds with one stone. There is a solution for both issues in the same code, oversimplified, of course:

public class MyReloadableResourceBundleMessageSource extends ReloadableResourceBundleMessageSource {

    protected String getMessageInternal(String code, Object[] args, Locale locale) {
        String message = super.getMessageInternal(code, args, locale);
        if (message == null) {
            message = super.getMessageInternal(code, args, getFallbackLocale(locale));
        }
        return message;
    }

    private Locale getFallbackLocale(Locale locale) {
        SessionService sessionService = (SessionService) Registry.getApplicationContext().getBean("sessionService");
        String currentSite = sessionService.getCurrentSession().getAttribute("currentSite");
        if (currentSite.equals("Belarus")) {
            if (locale.equals(ENGLISHLOCALE)) {
                return RUSSIANLOCALE;
            }
        }
        if (currentSite.equals("Ukraine")) {
            if (locale.equals(ENGLISHLOCALE)) {
                return UKRAINIANLOCALE;
            }
        }
        return locale;
    }
}

The next screenshot demonstrates how it works with a slightly different demo setup:

Demo setup showing fallback language behavior

© Rauf Aliev, April 2017