In todayβs globalized web applications, itβs crucial to support multiple languages to reach a wider audience. Spring Boot makes it easy to implement internationalization (i18n), allowing you to externalize messages and tailor them based on user locale.
In this guide, you’ll learn how to implement internationalization in Spring Boot with full working examples using the package com.kscodes.springboot.

π What is Internationalization (i18n)?
Internationalization (abbreviated as i18n) is the process of designing an application to support various languages and regional formats without changing the codebase. This is often implemented using:
- Message resource bundles (
messages_en.properties,messages_fr.properties, etc.) - Locale resolvers to determine user locale
- Spring MVC to display locale-based content
π Step-by-Step Guide to Internationalization (i18n) in Spring Boot
π§± Step 1: Create Message Resource Files
Create src/main/resources/messages_*.properties files for each language you want to support.
messages_en.properties (Default – English)
|
1 2 3 4 5 |
welcome.message=Welcome to KSCodes! error.notfound=Resource not found |
messages_fr.properties (French)
|
1 2 3 4 5 |
welcome.message=Bienvenue sur KSCodes! error.notfound=Ressource non trouvΓ©e |
π§ Step 2: Configure MessageSource Bean
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package com.kscodes.springboot.config; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.ReloadableResourceBundleMessageSource; import java.nio.charset.StandardCharsets; @Configuration public class I18nConfig { @Bean public MessageSource messageSource() { ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); messageSource.setBasename("classpath:messages"); messageSource.setDefaultEncoding(StandardCharsets.UTF_8.name()); return messageSource; } } |
π Step 3: Configure LocaleResolver
Use AcceptHeaderLocaleResolver to pick locale from request header (e.g., Accept-Language).
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package com.kscodes.springboot.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver; import java.util.Locale; @Configuration public class LocaleConfig { @Bean public LocaleResolver localeResolver() { AcceptHeaderLocaleResolver resolver = new AcceptHeaderLocaleResolver(); resolver.setDefaultLocale(Locale.ENGLISH); return resolver; } } |
π Step 4: Create a REST Controller to Use i18n
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
package com.kscodes.springboot.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RestController; import java.util.Locale; @RestController public class GreetingController { @Autowired private MessageSource messageSource; @GetMapping("/greeting") public String getGreeting(@RequestHeader(name = "Accept-Language", required = false) Locale locale) { return messageSource.getMessage("welcome.message", null, locale); } @GetMapping("/error") public String getErrorMessage(@RequestHeader(name = "Accept-Language", required = false) Locale locale) { return messageSource.getMessage("error.notfound", null, locale); } } |
π¦ Step 5: Make a Test Call
Using curl or Postman:
|
1 2 3 4 |
curl -H "Accept-Language: fr" http://localhost:8080/greeting |
Response
|
1 2 3 4 |
Bienvenue sur KSCodes! |
Without header:
|
1 2 3 4 |
Welcome to KSCodes! |
π Optional: Using Query Params for Locale (SessionLocaleResolver)
If you prefer users to change language via query param (e.g., ?lang=fr), use:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@Bean public LocaleResolver localeResolver() { SessionLocaleResolver resolver = new SessionLocaleResolver(); resolver.setDefaultLocale(Locale.ENGLISH); return resolver; } @Bean public LocaleChangeInterceptor localeChangeInterceptor() { LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor(); interceptor.setParamName("lang"); return interceptor; } |
Also register the interceptor:
|
1 2 3 4 5 6 7 |
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(localeChangeInterceptor()); } |
Now you can call:
|
1 2 3 |
http://localhost:8080/greeting?lang=fr |
π Security and Performance Tips
- Use UTF-8 encoding for all
.propertiesfiles. - Avoid loading too many language files unnecessarily.
- Use caching for messages when using ReloadableResourceBundleMessageSource.
π‘ Best Practices for Internationalization in Spring Boot
- Use consistent keys across languages (
welcome.message,error.notfound). - Keep messages short and readable.
- Validate translated
.propertiesfiles with native speakers. - Handle fallback when the language is missing.
β Summary
- Use
@MessageSourceto load language-specific property files. - Use
LocaleResolverto detect user locale (via headers or query). - Implement
Accept-Languageor?lang=based language switching. - Property files should be named
messages_{lang}.properties.