Spring PetClinic Goes Global: Enhancing Accessibility with Multi-Language Support
Anuj Ashok Potdar

Anuj Ashok Potdar @anizmo

About: Software Engineer | Java & Android Enthusiast | Passionate about clean code, design patterns, and building fun projects | Always learning, always building.

Location:
Boston, Massachusetts
Joined:
Apr 24, 2025

Spring PetClinic Goes Global: Enhancing Accessibility with Multi-Language Support

Publish Date: Apr 27
4 2

Introduction

The Spring PetClinic application is a flagship example of Spring Boot best practices, used by developers worldwide to learn enterprise patterns. Recently, my pull request (#0c88f9) was merged, introducing a critical feature: full internationalization (i18n) support using URL-based language switching. This update not only makes the application accessible to non-English speakers but also modernizes its architecture for scalability. Let’s explore why this matters.


The Problem: Limited Language Support

Before this change, Spring PetClinic lacked robust support for multiple languages. While it included basic message properties, it didn’t fully leverage Spring’s i18n capabilities, making it difficult to:

  1. Switch Languages Dynamically: Users couldn’t change languages via the URL, a common requirement for global applications.
  2. Maintain Consistency: Hardcoded text in HTML files made translations error-prone and fragmented.
  3. Scale to New Languages: Adding a new language required manual updates across HTML templates, violating the DRY (Don’t Repeat Yourself) principle.

For a reference application like PetClinic, this was a missed opportunity to demonstrate scalable i18n practices.


The Solution: URL-Based Localization and Decoupled Text

The commit introduces three key improvements:

1. Dynamic Language Switching via URL Parameters

A new WebConfiguration class configures Spring’s LocaleResolver to read the lang parameter from URLs (e.g., ?lang=es). This allows users to bookmark or share language-specific links, aligning with RESTful principles.

@Configuration  
public class WebConfiguration implements WebMvcConfigurer {  
    @Bean  
    public LocaleResolver localeResolver() {  
        SessionLocaleResolver slr = new SessionLocaleResolver();  
        slr.setDefaultLocale(Locale.ENGLISH);  
        return slr;  
    }  

    @Bean  
    public LocaleChangeInterceptor localeChangeInterceptor() {  
        LocaleChangeInterceptor lci = new LocaleChangeInterceptor();  
        lci.setParamName("lang");  
        return lci;  
    }  

    @Override  
    public void addInterceptors(InterceptorRegistry registry) {  
        registry.addInterceptor(localeChangeInterceptor());  
    }  
}  
Enter fullscreen mode Exit fullscreen mode

2. Centralized Message Properties

All UI text was moved to messages.properties files (e.g., messages_es.properties, messages_fr.properties), decoupling content from presentation. This ensures translations are maintained in a single location.

Example (messages_es.properties):

welcome=¡Bienvenido a PetClinic!  
owners=Propietarios  
find_owners=Buscar Propietarios  
Enter fullscreen mode Exit fullscreen mode

3. HTML Template Cleanup

Hardcoded text in Thymeleaf templates was replaced with Spring’s #{...} expressions, enabling dynamic resolution based on the user’s locale:

<!-- Before -->  
<h2>Find Owners</h2>  

<!-- After -->  
<h2 th:text="#{find_owners}">Find Owners</h2>  
Enter fullscreen mode Exit fullscreen mode

Architectural Impact: Why This Matters

1. Scalability for Global Audiences

By supporting URL-driven language switching, PetClinic now serves as a blueprint for building globally accessible applications. Developers can easily add new languages by creating a messages_xx.properties file—no code changes required.

2. Separation of Concerns

Decoupling text from HTML templates follows the MVC pattern rigorously. Content editors can now manage translations without touching Java code or Thymeleaf markup.

3. Improved Maintainability

Centralized message properties reduce duplication and make it easier to spot missing translations. For example, adding support for Japanese (messages_ja.properties) becomes a trivial task.

4. Enhanced User Experience

Users can now share language-specific URLs (e.g., https://petclinic.com?lang=de), making the app more inclusive and user-friendly.


Lessons for Developers

  1. Design for Global Audiences Early: Baking i18n into your architecture from day one avoids costly refactors later.
  2. Leverage Framework Features: Spring’s LocaleResolver and Thymeleaf’s i18n integration simplify what could otherwise be a complex task.
  3. Prioritize Clean Templates: Keep text out of HTML/CSS/JS—it belongs in resource files.

Community Impact

This commit closes issue #1854, addressing a long-standing request from contributors. By merging this, PetClinic:

  • Encourages participation from non-English speakers.
  • Demonstrates Spring’s i18n capabilities as a learning tool.
  • Sets a precedent for other open-source projects to prioritize accessibility.

Conclusion

Internationalization isn’t just about translating text—it’s about architecting applications to embrace diversity. This update ensures Spring PetClinic remains a modern, inclusive example for developers worldwide.

Check out the full commit here and consider contributing your own translations!

Comments 2 total

  • Nevo David
    Nevo DavidApr 28, 2025

    this is actually super helpful - real talk, the amount of times ive seen apps ignore language stuff is insane. you think more teams should bake in i18n from the start or fix it after they launch?

    • Anuj Ashok Potdar
      Anuj Ashok PotdarApr 28, 2025

      Hey Nevo, thanks for sharing your thoughts. My honest take is, starting early definitely helps save a lot of time especially in an enterprise application where you expect to have many contributors work on it.
      It becomes an exhausting task to change text from string literals to tokens once the project is big but if you start it off as a process to begin with, as and when your app gets bigger you can always enforce it and build it up piece by piece.

      I have followed this practice for real world applications that have been deployed for 20-30 countries and I have always felt I should have started sooner. :)

Add comment