Internationalization (i18n) in Spring Boot: A Complete Guide

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.

Internationalization (i18n) in Spring Boot: A Complete Guide

๐ŸŒ 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)


welcome.message=Welcome to KSCodes!
error.notfound=Resource not found

messages_fr.properties (French)


welcome.message=Bienvenue sur KSCodes!
error.notfound=Ressource non trouvรฉe

๐Ÿ”ง Step 2: Configure MessageSource Bean


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).


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


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:


curl -H "Accept-Language: fr" http://localhost:8080/greeting

Response


Bienvenue sur KSCodes!

Without header:


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:


@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:


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

Now you can call:


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.