Spring Boot mainly uses Maven or Gradle build systems to add dependencies by inheritance. Meanwhile, it inherits the excellent elements of Spring framework, reduces the complex configuration of Spring MVC architecture, and has built-in Tomcat and Jetty containers. Using Java Application to run programs instead of the traditional WAR package in a container such as Tomcat simplifies and speeds up the development process. In addition, Spring Boot learning is simple, lightweight, and easy to expand. With these outstanding features, Spring Boot has become a leader in the burgeoning field of rapid application development.

In an era when the Internet is evolving, it is imperative that an application be used on a global scale. The traditional programming method hardcodes translatable information such as label of menu button, prompt information, help document and other text information into the program code, but these can not adapt to the development of globalization well, and the program has poor expansibility, high maintenance cost. An application that can support globalization must implement a Single Executable program that uses resources dynamically.

For an application that can support globalization, consider the following three aspects of design, as shown in Figure 1.

Figure 1. Multilingual application model

  • Locale Model: The Locale Model is the basis of a multilingual application, used to determine the interface Language, date and time formatting methods, usually including Language Locale and Cultural Locale. There are several common ways to obtain a Locale for an application:

  • Locale from the user’s current operating system: locale.getdefault (); .

  • Retrieved from the locale set by the user’s browser.

  • Application custom language selection interface.

  • Other advanced methods, such as automatically selecting the language region according to the user input method, so as to display the corresponding interface language.

Which method an application chooses to capture Locale information depends on the user requirements of the application.

  • Externalization of resource files: This mainly refers to the interface language, which is automatically displayed according to the Locale model chosen by the user to make the customer feel that the product is designed for them.

  • Multi-cultural support of date and time, including currency, calendar, time, date, sorting, bi-directional interface and other display methods that conform to the custom of each country.

Here’s how Spring Boot-based applications support multilingual internationalization, including RESTful messages and content, from the above three aspects.

Before customizing application Locale models, you need to understand the principles of the region parser in Spring Boot. In Spring framework-based applications, users’ region information is identified by LocaleResolver, a web-based region parser interface provided by the Spring framework. Allows you to modify the locale through HTTP requests and responses, as shown in Listing 1.

Listing 1. The zone parser in the Spring framework

public interface LocaleResolver {
    Locale resolveLocale(HttpServletRequest request);
    void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale);
}
Copy the code

LocaleResolver has four specific implementations in the Spring framework:

  • According to the HTTP request header parsing area (AcceptHeaderLocaleResolver) : The default locale parser adopted by Spring is based on the request header, which parses the locale by examining the Accept-Language header of the HTTP request, which is determined by the user’s Web browser Settings.
  • Session attribute Resolution of regions (SessionLocaleResolver) : It parses regions by verifying preconfigured attributes in the user’s session. If the session attribute does not exist, it determines the default region based on the Accept-Language HTTP header.
  • Cookie by Cookie resolution region: If cookies exist, it determines the default region based on the Accept-LANGUageHTTP header.
  • FixedLocaleResolver: This parser always returns a fixed default locale, usually taken from the JVM’s default locale.

In addition to the four implementations provided in the Spring framework, you can also create custom zone parsers. You can see the code in Listing 2 in Spring Boot automatic configuration.

// add the LocaleResolver object @bean@conditionalonmissingbean@conditionalonProperty (prefix ="spring.mvc",
        name = {"locale"}
    )
    public LocaleResolver localeResolver() {
        if (this.mvcProperties.getLocaleResolver() == org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties.LocaleResolver.FIXED) {
            return new FixedLocaleResolver(this.mvcProperties.getLocale());
     } else {
            AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
            localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
            returnlocaleResolver; }}Copy the code

When our application needs a custom zone resolver (LocaleResovler), we can do so through the following steps.

** Step 1: Customize the zone parser **

The custom region parser is an implementation of the LocaleResolver interface in Spring. It can be based on application requirements and can be obtained from the user – defined language selection interface, user operating system, or browser language Settings. Listing 3 is an example of determining whether the user request contains the lang parameter and, if so, using the region information carried by the parameter; If not, it is taken from the Accept-language information in the browser request header.

Listing 3. Custom zone resolver

public class CustomLocaleResolver implements LocaleResolver{
 
@Override
public Locale resolveLocale(HttpServletRequest request) {
        String paramLanguage = request.getParameter("lang");
        if(! StringUtils.isEmpty(paramLanguage)){ String[] splits = paramLanguage.split("-");
            return new Locale(splits[0], splits[1]);
        }else{
            String acceptLanguage = request.getHeader("Accept-Language").split(",") [0]; String[] splits = acceptLanguage.split("-");
            returnnew Locale(splits[0], splits[1]); } // Locale.getdefault ()} @override public void if you want to use the current system languagesetLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
    }
}
Copy the code

Step 2: Add a custom zone parser to the IOC container

Usually added to a custom config file, the following example will customize the CustomLocaleResolver via @bean

Note added to the IOC container, as shown in Listing 4.

Listing 4. Adding a custom zone parser to IOC

@Configuration
public class CustomMvcConfigure extends WebMvcConfigurationSupport {
 
@Bean
public LocaleResolver localeResolver() {returnnew CustomLocaleResolver(); }}Copy the code

In this way, we can call our custom zone resolver in the program.

Multilingual support for the Thymeleaf template engine

Thymeleaf is an open source template engine that supports XML, XHTML, and HTML5 under the Apache License 2.0 License. It is mainly used for application development in Web or non-Web environments. It can run in both online and non-online environments. It can view static or dynamic pages on the browser side. This is because it supports HTML prototypes and then adds additional attributes to HTML tags to achieve the template + data presentation. Browsers ignore undefined tag attributes when parsing HTML, so the Thymeleaf template runs statically; When data is returned to the page, the Thymeleaf tag dynamically replaces the static content and makes the page display dynamically.

In the Spring MVC framework, we typically use JSP to present the Web front end. JSP is essentially a template engine, but Spring Boot officially recommends using the Thymeleaf template engine. Thymeleaf is a perfect substitute for JSP or other templating engines such as Velocity, FreeMarker, etc. Spring officially recommends using Thymeleaf, but that doesn’t mean Spring Boot doesn’t support JSP.

Here are the steps to support multiple languages using the Thymeleaf template in a Spring Boot project:

Step 1: Encapsulate the user locale

In our experiment, we designed a simple login page with a drop-down list of language selection, which will display the label on the login page using the language selected by the user, as shown in Figure 2.

Figure 2. Resource file organization structure

To enable our program to use the specified Locale, we first need to add the LocaleResolver Bean. In our example, we use the Locale chosen by the user, so we need to configure the LocaleChangeInterceptor interceptor, which mainly checks incoming requests. If the request has lang parameters (configurable), Such as http://localhost:8089/login? Lang =zh_CN, then the Interceptor uses localResolver to change the user’s default Locale. Listing 5 shows sample code that displays the interface based on the language the user chooses.

Listing 5. Custom LocaleResolver

 @Configuration
@ComponentScan(basePackages = "com.example.config")
public class MvcConfig implements WebMvcConfigurer {
     @Bean
        public LocaleResolver localeResolver() {
            SessionLocaleResolver slr = new SessionLocaleResolver();
            slr.setDefaultLocale(Locale.US);
            return slr;
        }
 
        @Bean
        public LocaleChangeInterceptor localeChangeInterceptor() {
            LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
            lci.setParamName("lang");
            returnlci; } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(localeChangeInterceptor()); }}Copy the code

Step 2: Define multilingual resource files

By default, resource files are placed directly in the SRC /main/resource directory. To structure the code, we create an i18n folder in the resource directory and then create resource files in the i18n directory (you can also create different subdirectories in the i18n directory). In this case, we need to respecify the basename: spring.messages.basename of the resource file in the application.properties configuration file. The resource file name can be defined according to your project, but the general specification is: module name_language_country.properties, which we named in this example like log_zh_cn.properties, and the directory structure of the resource file is shown in Figure 3.

Figure 3. Resource file organization structure

Each property key in the corresponding resource file generally starts with a lowercase letter, with an underscore to indicate its hierarchy within the program, and is sorted alphabetically for easy management and lookup, as shown in Listing 6.

Listing 6. An example English resource file

login_home=Home
login_login=Log In
login_login_page=Login page
login_languge_selector=Language Selector
login_please_select_language_to_display=Please select the language to display
login_password=Password
login_username=Username
Copy the code

Step 3: Invoke multilingual resources in the Thymeleaf template engine

To use the Thymeleaf template in the Spring Boot architecture, you first need to introduce the Thymeleaf template dependency pring-boot-starter-Thymeleaf in pum.xml, as shown in Listing 7.

Listing 7.pom.xml introduces dependencies

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-thymeleaf</artifactId>
 </dependency>
Copy the code

In addition, you need to configure the Thymeleaf view parser in application.properties, as shown in Listing 8.

Listing 8. Configuring Thymeleaf

#============ thymeleaf ====================================
spring.thymeleaf.mode=HTML
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.servlet.content-type=text/html
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.cache=false

Copy the code

Write the Thymeleaf template HTML file based on your business requirements. The default template mapping path is: SRC/main/resources/templates, Can also in the application. The properties configured in the custom template path spring. The thymeleaf. Prefix = classpath: / templates/myTemplates /.

In the Thymeleaf template file, extract all the hard-coded strings into the resource file, and use the Key value #{Property Key} from the resource file to represent the tag information displayed on the interface:

Normal tag string: use th:text= “#{login_login}”; For example, value=”Login” represents th:value=”#{login_login}”. Such as placeholder=”Login”, which is th:placeholder=”#{login_login}” listing 9 is the content of the Thymeleaf template for a simple Login page (which only lists some of the key code), All the displayed label information is extracted to the resource file, for example, th:text=”#(login_login_page)”, so that the string in the resource file is automatically read and displayed according to the language selected by the user.

Listing 9. Write the Thymeleaf template file

<body>
<div layout:fragment="content" th:remove="tag">
 <div class="row">
    <div class="col-md-6 col-md-offset-3">
       <h1 th:text="#{login_login_page}"></h1>
          <form th:action="@{/login}" method="post">
          <div class="form-group">
          <label for="username" th:text="#{login_username}"></label>:
          <input type="text" id="username" name="username" class="form-control" autofocus="autofocus" th:placeholder="#{login_username}">
          </div><div class="form-group">
           <label for="password" th:text="#{login_password}"></label>:
           <input type="password" id="password" name="password"
               class="form-control" th:placeholder="#{login_password}">
           </div> <div class="form-group">
           <div class="row">
              <div class="col-sm-6 col-sm-offset-3">
              <input type="submit" name="login-submit" id="login-submit"
               class="form-control btn btn-info" th:value="#{login_login}">
            </div></div></div>
           </form></div></div>
  
        <div class="row"><div class="col-md-6 col-md-offset-3">
          <div class="form-group">
           <label th:text="#{login_languge_selector}"></label>
            <select class="form-control" id ="locales">
   <option  value="" th:text="#{login_please_select_language_to_display}"></option>
   <option th:selected="${displayLang=='en_US'}" value="en_US">English</option>
     <option th:selected="${displayLang=='zh_CN'}" value="zh_CN"> </option> <option th:selected="${displayLang=='ja_JP'}" value="ja_JP"> </option> <option th:selected="${displayLang=='zh_TW'}" value="zh_TW"Traditional Chinese > < option > < / select > < / div > < / div > < div > < / div > < / div > < / div > < / body >Copy the code

After a step by step investigation, the cause was that the path of the resource file was incorrectly configured in the configuration file application.properties, as shown in Figure 5. The correct path is spring.messages.basename=i18n/login.

Figure 5. spring.messages.basename path is incorrect

Time and date formatting in Spring Boot

Java 8 provides more convenient date and time apis such as LocalDate, LocalTime, and LocalDateTime. The new date and time APIS in Java 8 are recommended for Spring Boot architecture. The advantages of LocalDate over Date class are as follows:

  • The Date printed by the Date class is not readable and usually needs to be formatted using SimpleDateFormat. The default LocalDate format is YYYY-MM-DD.
  • LocalDate is easier to use than Date in Date calculation and formatting.
  • The Date class contains both Date and time, while LocalDate, LocalTime, and LocalDateTime represent Date, time, Date, and time, respectively, and are very flexible to use.
  • The Date class does not support multithreaded safety, and new interfaces such as LocalDate are thread-safe.

For time and date formatting problems in background Java programs, the time and date formatting interface is often recustomized, such as passing in Locale parameters, style parameters that you want to display. Listing 10 is an example interface for formatting dates and times, and Listing 11 is an example implementation, with only a few typical formatting cases listed.

Listing 10. Time and date formatting interface

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Locale;
public interface I18nFormatter {
    String formaFullDateTime(LocalDateTime date, Locale locale);
    String formatFullDate(LocalDate originalDate, Locale locale);
     
    String formatMediumDateTime(LocalDateTime date, Locale locale);
    String formatMediumDate(LocalDate originalDate, Locale locale);
    String formatMediumDateShortTime(LocalDateTime date, Locale locale);
    String formatMediumTime(LocalTime originalDate, Locale locale);
 
    String formatShortDateTime(LocalDateTime originalDate, Locale locale);
    String formatShortDate(LocalDate originalDate, Locale locale);
    String formatShortTime(LocalTime originalDate, Locale locale);
    String formatShortDateMediumTime(LocalDateTime originalDate, Locale locale);
Copy the code

Listing 11. Time and date formatting implementation

@Service("I18nFormatter")
public class I18nFormatterImpl implements I18nFormatter { 
 
@Override
public String formatFullDate(LocalDate originalDate, Locale locale) {
    DateTimeFormatter dateFormat =DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).withLocale(locale);
    return dateFormat.format(originalDate);
}
 
@Override
public String formatMediumDateTime(LocalDateTime date, Locale locale) {
    DateTimeFormatter dateFormat = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.MEDIUM).withLocale(locale);
    return dateFormat.format(date);
}
 
 
@Override
public String formatShortTime(LocalTime originalDate, Locale locale) {
    DateTimeFormatter dateFormat = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).withLocale(locale);
    returndateFormat.format(originalDate); }}Copy the code

In the Spring Boot architecture, data is transmitted in JSON format between interfaces and between front and back ends. For date-format data, if the default is not processed, the legibility and availability is not very good. Listing 12 is the default JSON format for dates. The requested JSON-formatted date is poorly readable, as shown in Figure 6.

Listing 12. The default time date

@RestController
public class LocalDateTimeController {
     
    @GetMapping("/time")
    public DateTime timeMapping() {
        return new DateTime();
    }
 
public class DateTime {
     
    protected LocalDate localDate;
    protected LocalDateTime localDateTime;
    protected LocalTime localTime;
     
    public DateTime() {
        localDate = LocalDate.now();
        localDateTime = LocalDateTime.now();
        localTime = LocalTime.now();
    }
     
    public LocalDate getLocalDate() {
        return localDate;
    }
 
    public LocalDateTime getLocalDateTime() {
        return localDateTime;
    }
     
    public LocalTime getLocalTime() {
        return localTime; }}Copy the code

Figure 6. Json-formatted dates returned by the default format

Now we serialize the attribute values using @jsonFormat, as shown in Listing 13.

Listing 13. Annotated using @jsonFormat

public class JsonDateTime extends DateTime {
 
    @Override
    @JsonFormat(pattern="yyyy-MM-dd")
    public LocalDate getLocalDate() {
        return super.localDate;
    }
 
    @Override
    @JsonFormat(pattern="yyyy-MM-dd HH:mm")
    public LocalDateTime getLocalDateTime() {
        return super.localDateTime;
    }
     
    @Override
    @JsonFormat(pattern="HH:mm")
    public LocalTime getLocalTime() {
        return localTime;
    }
Copy the code

Figure 7. Json-formatted date with @jsonFormat

In Listing 13, we just formatted the JSON-formatted date, but we haven’t made it multilingual yet. We format the JSON-formatted date using the multilingual formatting method defined in Listing 10, defining the date parameter as a String. See Listing 14.

Listing 14. Using multilingual formatting methods

@RestController
public class LocalDateTimeController{
     
    @GetMapping("/g11nDateTime")
    public G11nDateTime g11nDateTimeMapping(@RequestParam(value = "language") String language) {
        Locale locale = new Locale("en"."US");
        if(! StringUtils.isEmpty(language)){ String[] splits = language.split("_");
                locale = new Locale(splits[0], splits[1]);
        }
        return new G11nDateTime(locale);
     
    }
 
    public class G11nDateTime {
 
     protected String localDate;
     protected String localDateTime;
     protected String localTime;
     
     public G11nDateTime(Locale locale) {
        I18nFormatterImpl formatter = new I18nFormatterImpl();
         
        localDate = formatter.formatFullDate(LocalDate.now(), locale);
        localDateTime = formatter.formatMediumDateShortTime(LocalDateTime.now(), locale);
        localTime = formatter.formatShortTime(LocalTime.now(), locale);
    }
     
     
     public String getLocalDate() {
        return localDate;
    }
 
    public String getLocalDateTime() {
        return localDateTime;
    }
     
    public String getLocalTime() {
        return localTime; }}Copy the code

When the language parameter passed in is zh_CN, the date of the response is shown in Figure 8.

Figure 8. Multilingual JSON-formatted dates

Spring Boot RESTful API supports multiple languages

With the rapid development of Spring Boot, based on RESTful

Quasi-microservice interfaces are also becoming more widely used, with RESTful apis using the HTTP protocol to represent create, retrieve, update, and delete (CRUD) operations. The following describes how to implement the multi-language support of server-side RESTful apis in the Spring Boot framework, mainly involving returned content and messages. There are usually several aspects to consider, as shown in Figure 9.

Figure 9. RESTful API multilanguage support

Step 1: Encapsulate and customize the resource file reader

Most existing development frameworks provide interfaces to read resource files. When it comes to reading resource files, ResourceBundle is a mechanism used to automatically read corresponding resource files based on different user area information. ResourceBundle is a Java resource file reading interface. Figure 10 summarizes the management mechanism for ResourceBundles in a Java application.

Figure 10. Java program Resource Bundle management flow

MessageSource HierarchicalMessageSource and ApplicationContext two interface inheritance, is shown in figure 11.

Figure 11. MessageSource class diagram

ResourceBundleMessageSource and ReloadableResourceBundleMessageSource is based on Java ResourceBundle HierarchicalMessageSource Two concrete implementation classes of. The difference between them and ResourceBundles is that they do not need to load resource files in different languages. BaseName can be used to load the entire set of international resource files. At the same time no longer need to display the call MessageFormat method, more simple and convenient to use; You can also read resource files in XML format.

ReloadableResourceBundleMessageSource can refresh the resource files regularly, so that in the case of the application does not restart perception resource file of the update, you can also set a read resource file encoding. Let ReloadableResourceBundleMessageSource cacheSeconds attributes according to the set time to refresh the monitor resource file for updates, but the refresh cycle can’t be too short, otherwise affect the performance of the application. If cacheSeconds set 1, said never refresh, ReloadableResourceBundleMessageSource and ResourceBundleMessageSource function at this time.

Listing 15 in ReloadableResourceBundleMessageSource class, on the basis of custom resource file read interface, read from the path of a resource file.

Listing 15. Custom resource file reading interface

public class CustomizeMessageResource {
 
    private final static Logger logger = LoggerFactory.getLogger(CustomizeMessageResource.class);
    private static MessageSourceAccessor accessor;
    private static final String PATH_PARENT = "classpath:i18n/";
    private static final String SUFFIX = ".properties";
     
    public CustomizeMessageResource() {}
     
    private void initMessageSourceAccessor() throws IOException{
        logger.info("init initMessageSourceAccessor...");
         
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        Resource resource = resourcePatternResolver.getResource(PATH_PARENT + "message" + SUFFIX);
        String fileName = resource.getURL().toString();
        int lastIndex = fileName.lastIndexOf(".");
        String baseName = fileName.substring(0,lastIndex);
         
        ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource = new ReloadableResourceBundleMessageSource();
reloadableResourceBundleMessageSource.setBasename(baseName);
reloadableResourceBundleMessageSource.setCacheSeconds(1800);
reloadableResourceBundleMessageSource.setDefaultEncoding("UTF-8");
      accessor = new MessageSourceAccessor(reloadableResourceBundleMessageSource);
    }
 
    public String getMessage(String key, String lang) throws IOException {
        initMessageSourceAccessor();
        Locale locale = new Locale("en"."US");
         if(! lang.isEmpty()) { locale = new Locale(lang.split("_")[0], lang.split("_") [1]); }return accessor.getMessage(key, null, "No such Property key", locale);
      }
     
    public String getMessage(String key, String lang, Object... parameters) throws IOException {
        initMessageSourceAccessor();
        Locale locale = new Locale("en"."US");
        if(! lang.isEmpty()) { locale = new Locale(lang.split("_")[0], lang.split("_") [1]); }return accessor.getMessage(key, parameters, "No such Property key", locale);
    }
Copy the code

Step 2: Design a multilingual API return status code

A RESTful API has a return status code. To support multiple languages, you need to consider the support of multiple languages when designing a return status code interface. The following example describes how to design the return status code and encapsulate the return interface. When designing the return status code, it involves the contents of the displayed message, represented here by a Property Key in the resource file, which makes it easier to read the return messages in different languages dynamically in the return status wrapper class, as shown in Listing 16.

Listing 16. The multilingual API returns a status code

public enum G11nUploadCode {
     
OK(200, "OK"."api_upload_response_ok"),    
ERROR(400, "WRONG REQUEST"."api_upload_response_wrong_request"),
CREATED(201, "CREATED"."api_upload_response_create"),
UNAUTHORIZED(401, "UNAUTHORIZED"."api_upload_response_unauthorized"),
FORBIDDEN(403, "FORBIDDEN"."api_upload_response_forbidden"),
NOT_FOUND(404, "NOT FOUND"."api_upload_response_not_found");
     
    private int code;
    private String status;
    private String propertyKey;
 
    private G11nUploadCode(int code, String status, String propertyKey) {
        this.code = code;
        this.status= status;
        this.propertyKey = propertyKey;
    }
 
    public void seCode(int code) {
        this.code = code;
    }
     
    public int getCode() {
        return this.code;
    }
 
    public String getStatus() {
        return this.status;
    }
     
    public void seStatus(String status) {
        this.status = status;
    }
 
    public void setPropertyKey(String propertyKey) {
        this.propertyKey = propertyKey;
    }
     
    public String getPropertyKey() {
        returnthis.propertyKey; }}Copy the code

Step 3: Multilingual API returns interface encapsulation

Use the custom resource file reading tool from step 1 to dynamically read the excess return information, as shown in Listing 17.

Listing 17. The multilingual API returns a status code

Spring defines a MessageSource interface to access resource files, which has several important methods for reading resource files, as shown in Table 1.

Table 1. MessageSource Interface description

public class G11nUploadResult implements Serializable {
 
private static final long serialVersionUID = 1L;    
private int code;
private String status;
private Object data;
     
public void setCode(int code) {
        this.code = code;
    }
         
public int getCode() {
    return this.code;
}
 
public void setStatus(String status) {
    this.status = status;
}
     
public String getStatus() {
    return this.status;
}
     
public void setData(Object data) {
    this.data = data;
}
     
public Object getData() {
    return this.data;
}
     
public G11nUploadResult() {} public G11nUploadResult(int code, String status, Object data) { this.code = code; this.status = status; this.data = data; } public G11nUploadResult(G11nUploadCode responseCodeI18n, String language) throws IOException{ CustomizeMessageResource customizeMessageResource = new CustomizeMessageResource();  this.code = responseCodeI18n.getCode(); this.status = responseCodeI18n.getStatus(); System.out.println("Status: "+ this.status); this.data = customizeMessageResource.getMessage(responseCodeI18n.getPropertyKey(), language); }}Copy the code

Step 4: Call the multilingual return code in the controller

This step is shown in Listing 18.

Listing 18. Calling a multilingual return code in a controller

@RestController
@Api(value="uploadFiles")
public class UploadFilesController {
 
    private final Logger logger = LoggerFactory.getLogger(UploadFilesController.class);
 
    private static String UPLOADED_FOLDER = "/users/tester/upload/";
 
@PostMapping("/uploadfiles")
    public G11nUploadResult uploadFile(@RequestParam("file") MultipartFile uploadfile) throws IOException {
        logger.debug("Single file uploa!");
         
    G11nUploadResult result = new G11nUploadResult();
        CustomizeMessageResource customizeMessageResource = new CustomizeMessageResource();
 
       if (uploadfile.isEmpty()) {
        return new G11nUploadResult(G11nUploadCode.ERROR, "zh_CN");     
    }
 
    try {
        saveUploadedFiles(Arrays.asList(uploadfile));
    } catch (IOException e) {
        return new G11nUploadResult(G11nUploadCode.NOT_FOUND, "zh_CN");
    }
    logger.info("Successfully uploaded - " + uploadfile.getOriginalFilename());
    result.setStatus("OK");
    result.setData(customizeMessageResource.getMessage("success_upload"."zh_CN", uploadfile.getOriginalFilename()));
    returnresult; }}Copy the code

Figure 12 is the result of testing the uploaded file. When the API is called, the parameter passed is simplified Chinese (zh_CN), and the returned status and information are displayed in Chinese. The returned information is displayed in a language familiar to the developer for easy reading and viewing.

Figure 12. MessageSource class diagram

conclusion

This article summarizes how to develop a multi-language application and RESTful API under the Spring Boot framework. The principle of region model under Spring Boot architecture is described, and the application program’s own region model is customized based on the existing region model. Thymeleaf template engine support for multiple languages; Spring Boot time and date formatting; And Spring Boot RESTful API multi-language support practices. Hopefully, this article has provided some insight for you as you develop internationalized applications and microservices.

The resources

  • Thymeleaf 官网 : Thymeleaf template details.
  • Web Services Globalization Model: an article on IBM Developer that introduces the globalization model.
  • Introduction to Internationalization and Localization of Java programs: Describes how Java programs use internationalization annotations.
  • Internationalize REST messages and content: Introduces a solution for internationalizing REST messages and content.
  • Do it yourself to enhance internationalization on Spring-Boot: introduces internationalization practices.