avatarMikhail Portnov

Summary

This text provides a comprehensive guide to implementing internationalization (i18n) and validation in a Spring Boot backend system using Kotlin, including best practices, configuration, and code examples.

Abstract

The article "Backend with Spring & Kotlin: i18n and Validation" explores various techniques for implementing validation and internationalization in a Spring and Kotlin backend system. It begins with an introduction to internationalization (i18n) and its importance in designing and developing an application that can be easily adapted to different languages and regions. The article then discusses how to implement i18n in Spring Boot using message property files, ResourceBundleMessageSource, and LocaleResolver. It provides examples of how to create message property files for different languages, configure a Spring Boot controller to use ResourceBundleMessageSource, and handle locale-specific messages in a Spring Boot application.

The article also covers best practices for i18n, including using separate properties files for each language, using keys instead of hard-coded strings, and using placeholders in the message properties files. The article then moves on to validation, discussing the importance of ensuring the accuracy, completeness, and consistency of data input by users. It introduces Spring Validation, a framework provided by the Spring ecosystem that allows developers to define and enforce validation rules on user input data. The article provides examples of how to use Spring Validation annotations to define validation constraints on Java objects, including field-level validation constraints, and how to handle validation errors in a Spring Boot application.

In conclusion, the article highlights the importance of Spring Validation and i18n in building robust and user-friendly backend systems that can handle a wide variety of user input data from around the world. It encourages readers to incorporate Spring Validation and i18n into their development process and offers resources for further learning on Spring and Kotlin at Hyperskill, an educational platform provided by JetBrains.

Bullet points

  • Introduction to internationalization (i18n)
  • Implementing i18n in Spring Boot using message property files, ResourceBundleMessageSource, and LocaleResolver
  • Best practices for i18n
  • Introduction to validation
  • Using Spring Validation to define and enforce validation rules on user input data
  • Handling validation errors in a Spring Boot application
  • Importance of Spring Validation and i18n in building robust and user-friendly backend systems
  • Resources for further learning on Spring and Kotlin at Hyperskill, an educational platform provided by JetBrains.

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

Response:

{
  "message": "Benutzer erfolgreich gelöscht!"
}

Request:

DELETE localhost:8080/user/6
Accept-Language: en

Response:

{
  "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 valid

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

Bean Validation

Spring Beans

Spring actuator

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!

Backend
Validation
Spring
Kotlin
Technology
Recommended from ReadMedium