Enum translations in Spring MVC


I recently came across this requirement but it wasn’t immediately obvious how to implement it. I have an enum in my application which defines the locales supported. For instance you might setup enum values of EN, FR and ES. (if English, French and Spanish are the languages you have translation properties files for) I then wanted to display a dropdown on the user screen with the values of this enum as available languages to select.

First stab
Here is the enum in question and my first go at displaying it.

/**
 * Languages supported with translations for the UI. A user can view the application in one 
 * of these languages.
 * 
 * @author Ben Thurley
 */
public enum SupportedLanguage 
{
  EN, FR, ES;
}

I got this piece of JSP from an answer on Stack Overflow

<form:select path="supportedLanguage">
   <form:options/>
</form:select>

This worked ok except the select dropdown just displayed EN, FR and ES. I needed a way of translating these codes into text for the users current selected language. I did see some solutions suggesting doing this in the controller. You could read all values and create a custom map of options with the right piece of translated text. I didn’t like the idea of this though as it was adding translation logic to the controller when for every other element it’s taken care of by Spring. There had to be another way and as it turned out, there was.

Spring custom formatter
Spring MVC allows us to write our own custom formatters. I just needed one that would take my enum code and format it to the translated string equivalent for the users locale.

This is what I ended up with:

public class SupportedLanguageFormatter implements Formatter<SupportedLanguage>
{

  @Resource
  private MessageSource messageSource;
	
  @Override
  public String print(SupportedLanguage object, Locale locale) 
  {
    // Lookup message translation, no args, default of English
    return messageSource.getMessage("supported.language." + object.name().toLowerCase(), null, "English", locale);
  }

  @Override
  public SupportedLanguage parse(String text, Locale locale) throws ParseException 
  {
    return SupportedLanguage.parse(text);
  }
}

This formatter does a two way translation. The print method takes a SupportedLanguage and looks up the relevant translated String. This is easily located using the injected MessageSource. For the reverse operation the parse method takes in a code from the UI (such as EN or FR) and returns an instance of the SupportedLanguage enum.

I tweaked the enum a bit so the parsing was encapsulated within the SupportedLanguage enum. Here’s the final version.

public enum SupportedLanguage 
{
  EN, FR, ES;

  public static List<SupportedLanguage> list() 
  {
    return Arrays.asList(values());
  }
	
  public static SupportedLanguage parse(String value)
  {
    SupportedLanguage lang = null;
    	
    for(SupportedLanguage test : list())
    {
      if(test.name().equalsIgnoreCase(value))
      {
        lang = test;
    	break;
      }
    }
    	
    return lang;
  }  
}

We then register the new formatter with spring.

  <mvc:annotation-driven conversion-service="conversionService">
  </mvc:annotation-driven>

  <!-- Custom formatters -->
  <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="formatters">
      <set>
        <bean class= "org.languagetool.web.formatter.SupportedLanguageFormatter" />
      </set>
    </property>
  </bean>

Now the SupportedLanguage enum is ready to be used. I did this by registering a ModelAttribute in the controller.

@Controller
@RequestMapping(value="admin/users")
@SessionAttributes({"userForm", "authoritiesList", "supportedLanguagesList"})
public class UserController
{
  ...
  @ModelAttribute("supportedLanguagesList")
  public List<SupportedLanguage> getAllSupportedLanguages() 
  {
    return SupportedLanguage.list();
  }  
  ...
}

This can now be used in the JSP as a set of options, I also added in an empty option if no language is selected on a new user form.

<form:select path="supportedLanguage">
  <spring:message code="option.select" var="selectOptionText" />
  <form:option value="" label="${selectOptionText}" />
  <form:options items="${supportedLanguagesList}" />
</form:select>

That’s almost it, now you just have to remember to add the translations to the relevant properties files.

# Languages
supported.language.en=English
supported.language.es=Spanish
supported.language.fr=French
Advertisements

5 thoughts on “Enum translations in Spring MVC

  1. Carlos

    It’ll be nice if you can have a generic Enum formatter or similar… I have a project with tons of Enums and create a Formatter for everyone is a lot of work.

    Something like this should work out of the box: {enumName}.{enumValue}=Translated text

    1. I’ve only got one or two at the moment but this is a fair point. I might look into this in future. Maybe having an interface on the enum and then writing a translator for the generic type.

  2. dev

    Which version of spring have you used, because I’ve tried similiar approach with version 3.2.2.RELEASE and 3.1.4.RELEASE. But default EnumToStringConverter is always called to print out options.

  3. I’ve just checked back on my code for this and it was written with spring 3.1.1.RELEASE. I would’ve thought either of the versions you have would be ok. When you say “similar approach” I wonder what’s different. Perhaps you’ve missed a step?

  4. It feels strange that parsing the translated value back to enum is required. Currently I’m trying to extend the OptionsTag class so it provides my tag writer to the original OptionsTag object. My tag writer will do the translation of the option label.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s