Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Message Source Spring Boot: Why Isn’t It Working?

Having trouble using MessageSource in Spring Boot? Learn common pitfalls, file naming tips, and how to resolve issues with Swagger not displaying messages.
Frustrated Java developer debugging Spring Boot MessageSource error with missing messages.properties file and broken Swagger localization UI Frustrated Java developer debugging Spring Boot MessageSource error with missing messages.properties file and broken Swagger localization UI
  • 🌍 Over 40% of Java localization errors stem from misnamed or misplaced message files.
  • ⚙️ Spring Boot auto-configures MessageSource if placed correctly in resources but is easily overridden by user-defined beans.
  • 🐞 MessageSource doesn't apply to Swagger annotations, causing untranslated API documentation by default.
  • 🧰 REST APIs perform better using AcceptHeaderLocaleResolver over SessionLocaleResolver for locale-based responses.
  • 🚀 Moving to OpenAPI 3 with springdoc-openapi makes it much easier to localize Swagger docs.

Why Isn’t MessageSource Working in Your Spring Boot App?

If you struggle with Spring Boot localization using MessageSource—especially when Swagger still shows only English text—you are not alone. This guide explains how MessageSource works, common setup mistakes, why Swagger ignores localization by default, and how to make your Spring Boot APIs and their documentation work in multiple languages.


What Is MessageSource in Spring Boot?

MessageSource is the built-in interface Spring uses for handling messages that can change by language. It handles internationalization (i18n) by letting you define messages in .properties files that change based on the client's language.

For example, if a request includes a French language header (Accept-Language: fr), Spring can get a human-readable string from messages_fr.properties.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Typical ways to use MessageSource include:

  • Localizing REST API responses
  • Localizing UI components in server-rendered applications
  • Providing consistent multi-language support across controllers and services

Spring provides tools like MessageSourceResolvable and LocaleResolver to figure out which language to use for a message.


Spring Boot’s Auto-Configuration of MessageSource

One big benefit of Spring Boot is that it can set up components automatically if certain things are in place. MessageSource works this way too.

By default, Spring Boot looks for message files named like this:

  • messages.properties (default)
  • messages_en.properties (English, US/UK)
  • messages_fr.properties (French)
  • messages_es.properties (Spanish), etc.

All these files must be in src/main/resources. And they need to use UTF-8 encoding to handle international characters correctly. If Spring finds these files, it sets up a ResourceBundleMessageSource. This source then provides the translations when your app runs.

✅ Minimal setup:

# In application.properties
spring.messages.basename=messages
spring.messages.encoding=UTF-8

This setup is usually enough to start Spring Boot localization. But you can change it.


Common Configuration Pitfalls in Message Files

Even though auto-configuration is simple, some small mistakes can stop messages from loading.

1. Incorrect File Naming

  • Always use lowercase ISO-639 language codes: messages_fr.properties, not Messages-French.properties.
  • Linux systems care about case. This means messages_fr.properties and Messages_fr.properties are different files. This can make your build work on Windows, but fail on Linux in production.

2. Wrong Directory Location

Make sure all your .properties files are right inside src/main/resources. If they are in subfolders that are not set up with a custom basename, Spring will ignore them.

3. Ignoring UTF-8 Encoding

This is a problem especially when you use characters like emojis, accents (ñ, é), or Asian symbols (漢字). Add the following property globally:

spring.messages.encoding=UTF-8

Or, set the encoding yourself if you define a custom bean. If you don't, the text might get garbled or become impossible to read.

4. Missing Keys

When you use a key that doesn't exist with messageSource.getMessage(), it will throw a NoSuchMessageException error. To prevent your app from crashing or showing key names to users, always add a fallback:

messageSource.getMessage("key.name", null, "Default Text", locale);

Defining a Custom MessageSource Bean

Spring Boot's default MessageSource configuration works for most scenarios. But, if you need more features, such as:

  • Dynamic reloading of properties
  • Custom message bundle folder
  • Loading from non-classpath locations
  • Integration with external CMS/localization repositories

You will need to change the default setup:

@Bean
public MessageSource messageSource() {
    ReloadableResourceBundleMessageSource source = new ReloadableResourceBundleMessageSource();
    source.setBasename("classpath:messages");
    source.setDefaultEncoding("UTF-8");
    source.setCacheSeconds(3600); // Refresh every hour (development convenience)
    return source;
}

🧠 Note: When you declare this bean, Spring Boot's automatic setup for MessageSource stops working. Be careful and make sure to copy all the settings you need.


Injecting MessageSource in Your Application

After you set it up correctly, add the bean to any Spring component like this:

@Autowired
private MessageSource messageSource;

Then, get messages that change based on the language:

public String greetUser(Locale locale) {
    return messageSource.getMessage("greeting", null, locale);
}

This works well in service layers, controller layers, or anywhere else your app needs to show messages in different languages.


How to Resolve Locale

To have Spring figure out which language to use for each request:

Using AcceptHeaderLocaleResolver

This is good for REST APIs:

@Bean
public LocaleResolver localeResolver() {
    AcceptHeaderLocaleResolver resolver = new AcceptHeaderLocaleResolver();
    resolver.setDefaultLocale(Locale.ENGLISH);
    return resolver;
}

It reads the Accept-Language header (like fr-CA, en-GB). Then, it automatically finds the right language.

Supporting Manual Switching via Query Strings

Use a LocaleChangeInterceptor to change languages with a lang URL parameter:

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

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(localeChangeInterceptor());
}

Now, requests like /api/greet?lang=es will respond with a Spanish message.


Troubleshooting Spring Boot MessageSource

If messages still don't load, this list can help you find the problem:

  • ✅ Are .properties files in src/main/resources?
  • ✅ Is spring.messages.encoding=UTF-8 set?
  • ✅ Did you change the automatic bean setup and forget some settings?
  • ✅ Are you using the correct message keys? Typos lead to NoSuchMessageException.
  • ✅ Is the LocaleResolver accepting the client’s language correctly?

Also, add logging to your message loading code. This will help you see what is going wrong:

LOGGER.info("Resolved message: " + messageSource.getMessage("greeting", null, locale));

Why Swagger Doesn’t Show Localized Messages

Here's the main issue: MessageSource does not work with annotations that Swagger uses.

Swagger annotations such as:

@ApiOperation(value = "Greet the user")

are compiled into static strings. These annotations do not know about MessageSource or the language used when the app is running. So, even if you set language headers or use @RequestHeader("Accept-Language"), Swagger UI will not show translated text.

The OpenAPI community [^3] has confirmed this is a known limit of Swagger/OpenAPI.


How to Localize Swagger Annotations

There are ways around this. How much effort you put in depends on how much automation you want.

Method 1: Constant Indirection

Put translated annotation values into class constants:

@ApiOperation(value = SwaggerMessages.OPERATION_GREET)

Then, in a utility or constants file, get these values using MessageSource when the app starts.

public class SwaggerMessages {
    public static final String OPERATION_GREET = ResourceBundle.getBundle("messages", Locale.getDefault()).getString("swagger.greet");
}

➡️ Limit: This only sets values once when the app starts. It does not change them for each request.

Method 2: Custom Swagger Model

Some developers choose to grab Swagger description data using filters or post-processors. They then change these values based on translated text.

This requires:

  • Writing a plugin or component that processes Swagger model after generation
  • Reading localized values with MessageSource
  • Injecting them in place of default descriptions

This is not easy, but it gives you more control over localization.

Method 3: Adopt springdoc-openapi

A newer and more flexible choice is to move to OpenAPI 3 with springdoc-openapi.

Advantages:

  • Richer annotations and better extensibility
  • Ability to override description providers
  • Use JSON/YAML language overlays for i18n

Example:

summary:
  en: "Retrieve user"
  fr: "Récupérer l'utilisateur"

LocaleResolver Strategies: AcceptHeader vs Session

Pick the right approach for your API:

Resolver Best for How it works
AcceptHeaderLocaleResolver REST APIs, Mobile Apps Reads Accept-Language header
SessionLocaleResolver Web/MVC Apps Stores locale in HTTP session

For REST APIs, AcceptHeaderLocaleResolver is best because it does not keep state.

To allow both header and URL-based switching, use both:

  • Accept by default
  • LocaleChangeInterceptor as override

Testing MessageSource Effectively

Do not guess about i18n testing.

Unit Test Example

@Autowired
private MessageSource messageSource;

@Test
void testFrenchGreeting() {
    String message = messageSource.getMessage("greeting", null, Locale.FRENCH);
    assertEquals("Bonjour", message);
}

Integration Test (with @SpringBootTest)

@SpringBootTest
class LocalizationIntegrationTest {

    @Autowired
    private MessageSource messageSource;

    @Test
    void testSpanishGreeting() {
        Locale locale = new Locale("es");
        String msg = messageSource.getMessage("greeting", null, locale);
        assertEquals("¡Hola!", msg);
    }
}

Also, check that the fallback works when keys are missing.


Best Practices for Managing MessageBundle Files

Organize message files clearly. This helps avoid problems with them later.

✅ Use Namespacing:

user.login.success=Login successful.
user.login.failure=Invalid username or password.

✅ One Key, One Meaning:

Do not use the same key for many messages. This will cause problems.

✅ Cross-check Languages:

Write scripts to check if keys are in all language files. This helps avoid “missing key” errors when your app runs.

✅ Keep Formatting Consistent:

Do not misplace colons (:), tabs, or equals signs. These confuse the programs that read the files.


Tools for Debugging Spring Boot Localization

Use modern IDEs and plugins to help you:

  • 🔍 IntelliJ IDEA: Good support for looking at resource bundles and their values.
  • 📊 CheckStyle or Lint tools: Find duplicate keys and bad .properties files.
  • 📡 Spring Boot Actuator (custom endpoint): Show your messageSource keys through HTTP endpoints to do quick checks.
  • 📦 Diff Scripts: Use Python or Bash to find missing translations in different languages.

With careful setup, MessageSource and Spring Boot localization give you the tools to support apps for users around the world. The main thing is to know when Spring does things automatically and when you need to step in—especially with Swagger and OpenAPI.

Want to do more with internationalization? Download our working Spring Boot + Swagger example, localized in English, Spanish, and French.


Citations

[^1]: Spring.io. (2023). Spring Boot Reference Documentation – Internationalization. Retrieved from https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.internationalization

[^2]: Java Code Geeks. (2022). Top Localization Mistakes in Java Projects. Retrieved from https://www.javacodegeeks.com/2022/03/top-localization-mistakes-in-java.html

[^3]: OpenAPI Initiative. (2021). Developing Multilingual API Descriptions. Retrieved from https://github.com/OAI/OpenAPI-Specification/issues/332

[^4]: Baeldung. (2023). Handling Locale in Spring Boot. Retrieved from https://www.baeldung.com/spring-boot-internationalization

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading