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
messages_de_DE.properties
messages_de.properties
messages.properties
- 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.
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: 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>
<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>
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;
}
}
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;
}
}
© Rauf Aliev, April 2017
Ruslan Bespalov
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.
Ruslan Bespalov
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.