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
.properties
files. - 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
.properties
files with native speakers. - Handle fallback when the language is missing.
β Summary
- Use
@MessageSource
to load language-specific property files. - Use
LocaleResolver
to detect user locale (via headers or query). - Implement
Accept-Language
or?lang=
based language switching. - Property files should be named
messages_{lang}.properties
.