Backend with Spring & Kotlin: i18n and Validation
In the previous article on the development of Spring Boot Backend with Kotlin, we discussed how to connect our application to MySQL and work with it in the right way. Today, our goal is to understand how to make more advanced apps, using special techniques.

Developing a backend system requires more than choosing the right tools and languages. It also involves implementing robust internationalization (i18n) and validation features to ensure that the application can handle different inputs and languages. This is where Spring and Kotlin truly shine, as they offer developers a range of tools and techniques for implementing validation and i18n features.
This article will explore various validation and i18n techniques that can be implemented in a Spring and Kotlin backend system. We will discuss how to implement validation using Spring’s validation framework and Kotlin’s data classes. We will also explore how to implement i18n using Spring’s message source and Kotlin’s string templates. By the end of this article, you will have a solid understanding of how to build a robust and reliable backend system using Spring and Kotlin.
Being a part of Hyperskill, I should note that it is worth going through similar topics on Hyperskill to better understand the material we discuss today. By the way, there are many tracks on other languages and technologies, not only Kotlin and Spring :)
Introduction to i18n
Internationalization (i18n) is the process of designing and developing an application so that it can be easily adapted to different languages and regions without changing the core code. It involves separating the application’s user interface and the content from the code so that they can be easily translated and localized.
Implementing i18n in Spring Boot
To implement i18n in a Spring Boot application, create message property files for different languages. These files contain key-value pairs, where a key is a unique message identifier, and the value is the translated message in a specific language.
To use i18n messages in our application, we need to use Spring’s ResourceBundleMessageSource. The ResourceBundleMessageSource class is responsible for loading the message properties files, while the LocaleResolver interface determines the user’s preferred language.
Below is an example of how to create a configuration class, where we create the desired objects: LocaleResolver and MessageSource.
@Configuration
class MyAppConfig {
@Bean
fun localeResolver(): LocaleResolver {
val localeResolver = AcceptHeaderLocaleResolver()
localeResolver.defaultLocale = Locale.ENGLISH
return localeResolver
}
@Bean("messageSource")
fun messageSource(): MessageSource {
val messageSource = ResourceBundleMessageSource()
messageSource.setBasenames("language/messages")
messageSource.setDefaultEncoding("UTF-8")
return messageSource
}
}Also, we need to create files for our locales using the .properties format. We just create files with the correct suffixes for all the locales you want to support.
resources/language/messages.properties:
label.delete=User successfully deleted!resources/language/messages_de.properties:
label.delete=Benutzer erfolgreich gelöscht!Here’s an example of ResourceBundleMessageSource in a Spring Boot controller. Of course, we can add more logic tor our app, for example, adding exceptions in case of a request for a locale doesn’t exist, wrapping how we get the messages in a separate function, and putting it in an abstract class. But this is beyond the scope of our guide today.
@RestController
@RequestMapping("/user")
class UserController(
private val userService: UserService,
private val userMapper: UserMapper,
private val messageSource: ResourceBundleMessageSource,
) {
// ...
@DeleteMapping("/{id}")
fun deleteUser(@PathVariable id: Long): DeleteResponse {
userService.deleteUser(id)
val locale = LocaleContextHolder.getLocale()
val msg = messageSource.getMessage("label.delete", null, locale)
return DeleteResponse(msg)
}
// ...
}Request:
DELETE localhost:8080/user/5
Accept-Language: deResponse:
{
"message": "Benutzer erfolgreich gelöscht!"
}Request:
DELETE localhost:8080/user/6
Accept-Language: enResponse:
{
"message": "User successfully deleted!"
}In this example, we inject ResourceBundleMessageSource into our controller and use it to retrieve the greeting message based on the user’s preferred language.
Best Practices for i18n
When implementing i18n in your Spring Boot application, it’s important to follow the best practices to ensure that your code can be maintained and scaled. Here are some best practices to keep in mind:
- Use a separate properties file for each language to keep the translations organized.
- Use keys instead of hard-coded strings in the code to make it easier to update and maintain the translations.
- Use placeholders in the message properties files to allow for dynamic content, such as user names or dates.
Validation
Validation is a crucial aspect of any software application, as it helps to ensure the accuracy, completeness, and consistency of data input by users. Validation involves checking user input against predefined rules and constraints, such as ensuring that a user’s email address is valid or that the user age is within a specific range.
In a backend system, validation is essential to ensure that the data being processed is valid and meets the business rules and requirements of the application. Without proper validation, a system can be vulnerable to errors, data inconsistencies, and even security breaches.
Spring Validation
Spring Validation is a framework provided by the Spring ecosystem that allows developers to define and enforce validation rules on user input data. It is based on the Java Bean Validation (JSR-380) standard and provides a set of annotations that can be used to define validation constraints on Java objects.
Spring Validation is a powerful tool for ensuring the accuracy and completeness of data input in a Spring-based backend system. It allows developers to define validation rules for individual fields, as well as for the object as a whole. For example, developers can define constraints such as the maximum string length, the minimum and maximum number values, or whether a field is required.
Spring Validation can be easily integrated into a Spring application by adding the necessary dependencies and configuring a validator object. Once configured, developers can annotate their Java classes and fields with validation constraints, and the validator will automatically validate user input data against these constraints.
For this feature, add the dependency for build.gradle.kts:
implementation("org.springframework.boot:spring-boot-starter-validation")Implementation
First, we’ll define a data class for our data object, User:
class UserRequest(
@field:NotNull(message = "Name cannot be null")
@field:Size(min = 2, max = 50, message = "Name must be between 2 and 50 characters")
val name: String,
@field:NotNull(message = "Email cannot be null")
@field:Email(message = "Email must be valid")
val email: String,
)In this example, we’ve defined two fields for our User class: name and email. We’ve also added validation annotations to each field using the @field qualifier. The @NotNull annotation specifies that the field cannot be null, while the @Size annotation specifies the minimum and maximum length of the name field. Similarly, the @Email annotation specifies that the email field must be a valid email address.
Next, define a controller class that will handle requests to create a new user:
@RestController
class UserController(private val userService: UserService) {
// ...
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
fun createUser(@Valid @RequestBody user: UserRequest): UserResponse {
return userMapper.asResponse(userService.createUser(userMapper.asEntity(user)))
}
// ...
@ExceptionHandler(MethodArgumentNotValidException::class)
fun handleValidationException(ex: MethodArgumentNotValidException): ResponseEntity<String> {
val result = ex.bindingResult
val errors = result.fieldErrors
var message = ""
for (error in errors) {
message += "${error.defaultMessage}\n"
}
return ResponseEntity.badRequest().body(message)
}
}In this example, we’ve defined a UserController class with the createUser() method that handles POST requests to create a new user. The @Valid annotation on the user parameter specifies that the User object should be validated against the validation annotations defined in the User class. If any validation errors occur, Spring will throw MethodArgumentNotValidException. We handle it in the handleValidationException() method. This method extracts the validation error messages from the exception and returns them in a response entity with a 400 Bad Request status code.
Let’s try our updated endpoint with validation providing invalid data for it:
POST localhost:8080/user Content-Type: application/json
{
"name": "n",
"email": "[email protected]"
}Our application will return 400 HTTP Code and the next message:
Name must be between 2 and 50 characters
Email must be validWrapping up
In conclusion, Spring Validation is an essential tool for ensuring the accuracy and completeness of user input data in Spring-based backend systems. With the help of validation annotations, developers can easily define and enforce validation rules for individual fields and objects, and ensure that their applications can handle user input with confidence and reliability.
In addition, by incorporating internationalization (i18n) into your validation messages, we can provide a more user-friendly experience for users around the world. Using Spring’s MessageSource and LocaleResolver, we can easily define validation messages in multiple languages and automatically serve the appropriate message based on the user’s language preference.
Spring Validation and i18n can help create robust and user-friendly backend systems that can handle a wide variety of user input data from around the world. So, next time you’re building a Spring-based backend system, don’t forget to incorporate Spring Validation and i18n into your development process!
If you want to learn more about Spring Validation and how to use it correctly and not make mistakes, take a course on Spring at our educational platform Hyperskill!
Our main content provider, JetBrains, offers interactive and comprehensive tracks on Spring and Kotlin, as well as other programming languages and technologies.
Links to some topics where you can continue to learn Spring with good theory and practical problems and questions:
Also, if you want to study Kotlin, choose our tracks, Kotlin Basics, and Kotlin Developer!
I hope you found this article helpful. If you have any questions or feedback, let me know in the comments section below.
Thank you for reading and happy coding!






