Introducing GraphAware Hume 3.0. Explore the latest updates

Internationalisation with CypherMessageSource, Spring and Neo4j

September 29, 2016 · 6 min read

Whether you realise it or not, the software you build has global potential.

More than most other products, software can evolve from a small individual effort into something used worldwide. Even if that reach isn’t obvious at the start, it’s in your best interest to design with it in mind.

Maximising your impact means making your software accessible to as many people and organisations as possible. That’s why internationalisation and localisation are essential — they ensure your product can adapt to different languages, regions, and cultural contexts.

Internationalisation and localisation

Internationalisation is the process of developing products in such a way that they can be localised for languages and cultures easily. In practice, that means avoiding hardcoded text in your software. Instead, use a mechanism that dynamically displays content based on the user’s language and locale.

For example, rather than hardcoding “Hello” for all users, your application could display “Hello” for users in the US, “Bonjour” for users in France, and “Hola” for users in Mexico.

By adopting internationalisation and localisation early in development, you significantly reduce the time, effort, and cost required to support a global audience.

Spring support for internalisation and localisation

As with numerous other aspects of Java software development, the Spring framework offers excellent support for internationalisation and localisation and two implementations of the MessageSource interface, a primary component to make use of in your code, are provided out-of-the-box.

While these approaches work well, they typically rely on message definitions (such as “Hello”, “Bonjour”, or “Hola”) stored in property files on the filesystem.

However, property files can be limiting in terms of flexibility. As a result, developers often implement custom MessageSource solutions backed by a database (e.g. via JDBC).

This approach offers several advantages. Message definitions can be updated at runtime — quickly and easily — without requiring application rebuilds, redeployments, or restarts.

By storing message definitions in the database, they can be managed like any other application data. This often leads to the addition of administrative features within the application itself, allowing users to add, update, or delete localisation content through a built-in management interface.

The CypherMessageSource

The CypherMessageSource is a MessageSource implementation that uses the Cypher query language to load message definitions from a Cypher-compatible graph database, such as Neo4j.

Modelled after existing JDBC-based MessageSource implementations, the CypherMessageSource offers all the advantages of database-backed message definition storage for graph-based applications.

Using CypherMessageSource

At a high level, the steps required to use CypherMessageSource are:

  • Clone the source, build, and include the jar within your project
  • Register an instance of the CypherMessageSource within the ApplicationContext
  • Create the desired message definitions within the database
  • Localise and internationalizse the application by updating hard coded strings of text

Clone, build, include

When executed from within a Unix shell, the following commands will clone the project source and build the jar:

git clone https://github.com/espiegelberg/cypher-messagesource
cd cypher-messagesource
mvn clean install

Next, use your dependency management system, like Maven or Gradle, to add the jar to the application. The following example includes your locally generated jar in your project:

        <dependency>
            <groupId>com.mile24.</groupId>
            <artifactId>cypher-messagesource</artifactId>
            <version>1.0.0</version>
        </dependency>

Registering an instance

Modify the application to register an instance of the CypherMessageSource, with this JavaConfig for example:

@Configuration
public class ApplicationConfiguration {
...
	@Bean
   	public MessageSource messageSource() {
		MessageSource cypherMessageSource = new CypherMessageSource();
        return cypherMessageSource;
	}
…
}

Create the desired message definitions

At the graph database level, each message definition is stored as a single MessageDefinition node.

Taking advantage of the graph’s ability to store flexible key-value pairs, each node includes:

  • a code property that identifies the message, and
  • one or more locale-specific key-value pairs representing translations.

For example, a single node might store translations for multiple languages under different locale codes.

To create a message definition for an internationalised welcome message, you could use the following Cypher query:

create (n:MessageDefinition {code: 'welcome', en_US: 'Welcome', fr_FR: 'Bonjour', es_MX: 'Hola'})

The Cypher statement above can be adapted and reused to create any message definition within the system.

When you need to support a new language or locale, you simply add a new property to each MessageDefinition node. This property contains the locale code and its corresponding translation.

For example, to provide a welcome message for users who have selected Italian, you would update the existing ‘welcome’ MessageDefinition node using the following Cypher query:

match (n:MessageDefinition) where n.code = 'welcome' set n.it_IT = 'Ciao'

This process should be repeated for all desired localised and internationalised text in your system.

Localise and internationalise the application

Lastly, modify your application to remove hard coded strings of text that should be internationalised and localised.

For example, in a JSP-based application, replace the use of “Hello” with the use of Spring’s message tag library:

...
<spring:message code="welcome" />

As opposed to the previously hard-coded value of “Hello”, the above line uses Spring’s internationalisation and localisation infrastructure, which employs the registered CypherMessageSource, to present the current user with the message definition appropriate for their selected language and locale. The result is that users in the US will be presented with “Hello”, users in France with “Bonjour”, and users in Mexico with “Hola”.

At the service layer, hardcoded text can be replaced by injecting a MessageSource. This allows you to retrieve messages dynamically by providing a message code and the desired locale. For example:

@Service
public class FooService {
…
@Autowired
private MessageSource messageSource;
...
Object arguments[] = new Object[] {};
String name = messageSource.getMessage("welcome", arguments, Locale.US);
…
}

This dynamically generated string value can now be used within the service code just as the preceding hard-coded name value was.

Conclusion

Your software has a global market. By adopting internationalisation and localisation — processes that enable your application to support different languages and regions — you maximise the number of people and organisations that can use it.

While Spring provides the core infrastructure, CypherMessageSource extends this capability by using the Cypher query language to load message definitions from a graph database such as Neo4j.

This approach offers several advantages over traditional property files. Following the pattern of JDBC-backed MessageSource implementations, CypherMessageSource allows you to manage translations dynamically, making it easier and faster to internationalise and localise your graph-based application.

Biography

Eric Spiegelberg is the founder of Mile 24, a Twin Cities-based software consultancy in the US. As an architect with over 15 years of experience with the Java platform, Eric holds a BS in Computer Science and a MS in Software Engineering, is an avid technologist, a published technical author, and life long learner. Outside of technology, Eric is a high-performance and instrument-rated private pilot, has run the Paris Marathon in France, and enjoys an interest in travel.