Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Java/Spring, how to gracefully handle missing translation values?

I'm using Java + Spring for an international website.

The 2 languages are ZH and EN (Chinese and English).

I have 2 files: messages.properties (for English key/value pairs) and messages_zh.properties.

The site is coded in English using #springMessage tags. I then use Poeditor.com to have translators provide Chinese values for each English phrase. So, although the English messages.properties file is always complete, and although the messages_zh.properties file always has all of the keys, the messages_zh.properties file will sometimes have blank values because I receive translations from the translators several days later.

I need my system to display the equivalent English value (on my website) whenever the Chinese value is missing.

How can I tell Spring to "fall back" to use English whenever a Chinese value isn't available? I need this to happen on a value-by-value basis.

Right now, I see blank button labels on my site wherever a Chinese value is missing. I'd rather that English (the default language) be used whenever the Chinese is blank.

like image 557
Ryan Avatar asked Aug 09 '13 17:08

Ryan


2 Answers

You could create your own Custom MessageSource for this purpose.

Something like:

public class SpecialMessageSource extends ReloadableResourceBundleMessageSource {

      @Override
      protected MessageFormat resolveCode(String code, Locale locale) {
         MessageFormat result = super.resolveCode(code, locale);
         if (result.getPattern().isEmpty() && locale == Locale.CHINESE) {
            return super.resolveCode(code, Locale.ENGLISH);
         }
         return result;
      }

      @Override
      protected String resolveCodeWithoutArguments(String code, Locale locale) {
         String result= super.resolveCodeWithoutArguments(code, locale);
         if ((result == null || result.isEmpty()) && locale == Locale.CHINESE) {
            return super.resolveCodeWithoutArguments(code, Locale.ENGLISH);
         }
         return result;
      }
   }

and configure this messageSource bean in spring xml as

<bean id="messageSource" class="SpecialMessageSource">
.....
</bean>

Now to get resolved Label you will be invoking MessageSource's either of the below methods

String getMessage(String code, Object[] args, Locale locale);
String getMessage(String code, Object[] args, String defaultMessage, Locale locale);

resolveCode() will be called when your message label has arguments and you pass those arguments via args parameter like below
invalid.number= {0} is Invalid
and you invoke messageSource.getMessage("INVALID_NUMBER", new Object[]{2d}, locale)

resolveCodeWithoutArguments() will be called when your message label does not have arguments and you pass args parameter as null
validation.success = Validation Success
and you invoke messageSource.getMessage("INVALID_NUMBER", null, locale)

like image 94
harrybvp Avatar answered Sep 21 '22 20:09

harrybvp


@harrybvp's answer got me on the right track. This is the code that seems to work for me (and is unit-testable when you mock the methods that call "super"):

    public class GracefulMessageSource extends org.springframework.context.support.ReloadableResourceBundleMessageSource {

        final static private Locale defaultLocale = Locale.ENGLISH;

        @Override protected MessageFormat resolveCode(final String code, final Locale locale) {
            MessageFormat result = superResolveCode(code, locale);

            if (result.toPattern().isEmpty() && !locale.equals(this.defaultLocale)) {
                result = superResolveCode(code, this.defaultLocale);
            }

            return result;
        }

        @Override protected String resolveCodeWithoutArguments(final String code, final Locale locale) {
            String result = superResolveCodeWithoutArguments(code, locale);

            if (result.isEmpty() && !locale.equals(this.defaultLocale)) {
                result = superResolveCodeWithoutArguments(code, this.defaultLocale);
            }

            return result;
        }

        protected MessageFormat superResolveCode(final String code, final Locale locale) {
            return super.resolveCode(code, locale);

        }

        protected String superResolveCodeWithoutArguments(final String code, final Locale locale) {
            return super.resolveCodeWithoutArguments(code, locale);

        }

    }

and in my webmvc-config.xml:

<bean id="messageSource" class="com.mycode.fe.web.GracefulMessageSource">
    <property name="basename">
        <value>${content.path.config}/WEB-INF/messages</value>
    </property>
    <property name="defaultEncoding" value="UTF-8" />
    <property name="cacheSeconds" value="2"/>
    <property name="fallbackToSystemLocale" value="true"/>
</bean>

Then in my Velocity view templates:

For a normal key with no arguments: #springMessage('menu_item1')

For a key that accepts arguments: #springMessageText('male_to_female_ratio' [3, 2])

Then in the messages.properties (English translations): male_to_female_ratio = There is a {0}:{1} male-to-female ratio.

like image 33
Ryan Avatar answered Sep 21 '22 20:09

Ryan