Overcoming flaws of Fallback language mechanism


Introduction

Fallback language function is designed to show the content in language X if language Y if not available. It is better to show the content in a English than nothing. At least SAP hybris designers thought so. This mechanism is attribute-level: it works for attributes that don’t have a localization for the current locale the additional language versions are used. It is not working for whole items. For example, if your product has only two fully completed language versions and three partially completed, you can’t redirect partially completed language versions to a completed ones, it will work for each product attribute separately. If there is no explicit localization for a certain locale, the SAP Hybris Commerce relies on locales that are defined as fallback locales for that locale.

Challenges

Actually, there are two challenges: (1) Fallback mechanism is not compatible with Java Resource Bundle. SAP admits that there is a flaw in the fallback mechanism. According to documentation, “the fallback mechanism for de_DE is always de, [javaVMLocale]  regardless what has been specified within the Hybris system. “. They have an example:
  • 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 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 (e.g. it first checks for
messages_de_DE.properties
to
messages_de.properties
down to
messages.properties
in the end, being the overall default). For some configurations, the fallback mechanism won’t work at all. You will have a key on your webpage instead of actual value. For example, if you add a new key, and you don’t create a proper record for this key in the property files, the system will show a key itself instead of empty value. (2) hybris doesn’t 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 for the country) and
    • English language, and
  • there is a message that is not defined for English version of the website, and
  • you want to show a message in a default language of website X if it happens.
fallback1.png

Solution

In order to support the country-dependent language fallback, the solution is pretty simple: you need to use a complex language id that contains a country tag: fallback2.png It solves the second challenging issue. However, if you need to group languages over 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 can’t 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 bit different demo setup:2017-04-11_22h04_13.png

© Rauf Aliev, April 2017

2 Responses

  1. Ruslan Bespalov

    Ruslan Bespalov

    Reply

    21 April 2017 at 15:45

    A question about the last point. Shouldn’t the left screenshot have:
    – testKey1French
    – testKey2English
    – testKey3French
    ?
    At the moment both screenshot are identical.

  2. Ruslan Bespalov

    Ruslan Bespalov

    Reply

    21 April 2017 at 15:45

    A question about the last point. Shouldn’t the left screenshot have:
    – testKey1French
    – testKey2English
    – testKey3French
    ?
    At the moment both screenshot are identical.

Leave a Reply