[SpringBoot Foundation series] Implementation of international support instance development
Internationalization support, for app development partners should compare prices common; As the junior partner of Java backend, generally speaking, there are not too many opportunities to contact internationalization, after all, there are not too many overseas enterprises
SpringBoot offers international support, and tutorials are available online, but the actual experience is not as smooth as expected. This article will explain how SpringBoot supports nationalization and what to do in the process
I. Project environment
1. Project dependencies
This project is developed by SpringBoot 2.2.1.RELEASE + Maven 3.5.3 + IDEA
Open a Web service for testing
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
Copy the code
2. Configuration file
In the configuration file, specify internationalization parameters, thmeleaf configuration information
application.yml
spring:
messages:
basename: i18n/messages/messages
encoding: UTF-8
fallbackToSystemLocale: false
thymeleaf:
mode: HTML
encoding: UTF-8
servlet:
content-type: text/html
cache: false
Copy the code
3. Internationalize information files
The above configuration spring.messages.basename specifies the directory and prefix of the internationalization configuration file. The value is i18n/messages/messages
So in the resources directory, create a new file i18n/messages, internationalization file named message-xxx. properties, and the project result looks like this
For example, messages_zh_cn.properties in simplified Chinese
200=successful
500=Internal abnormal
name=The user name
pwd=password
Copy the code
English messages_en_US. The properties
200=success
500=unexpected exception
name=user name
pwd=password
Copy the code
Traditional messages_zh_TW. The properties
200=successful
500=Internal abnormal
name=The user name
pwd=password
Copy the code
instructions
Basename is the directory of the internationalized file with the prefix of the file name. For example, if the last layer of messages is missing, the configuration will be displayed
Secondly, in IDEA, after selecting the nationalization file, click the Resource Bundle below to enter a more friendly edit box as shown in the figure, which supports modifying information of multiple languages at a time
II. Internationalization support
How to obtain the values of different languages according to the key in the previous configuration?
1. MessageSource
In SpringBoot, MessageSource is used to obtain value information in different languages
As a most basic package
public class MsgUtil {
private static MessageSource messageSource;
public static void inti(MessageSource messageSource) {
MsgUtil.messageSource = messageSource;
}
/** * get a single internationalized translation value */
public static String get(String msgKey) {
try {
return messageSource.getMessage(msgKey, null, LocaleContextHolder.getLocale());
} catch (Exception e) {
returnmsgKey; }}}Copy the code
2. Test the demo
Next, write a basic test demo that modifies the value in LocalContextHolder based on the passing parameters to switch between languages
@Controller
@SpringBootApplication
public class Application {
public Application(MessageSource messageSource) {
MsgUtil.inti(messageSource);
}
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
@Data
@Accessors(chain = true)
public static class RspWrapper<T> {
private int code;
private String msg;
private T data;
}
@GetMapping(path = "change")
@ResponseBody
public String changeLocal(String language) {
String[] s = language.split("_");
LocaleContextHolder.setLocale(new Locale(s[0], s[1]));
RspWrapper res = new RspWrapper<>().setCode(200).setMsg(MsgUtil.get("200")).setData(true);
returnJSON.toJSONString(res); }}Copy the code
Demonstrate the following
3. Child thread support
It is possible to switch languages based on request parameters, but there is a problem that internationalization support in child threads does not work
@GetMapping(path = "change2")
@ResponseBody
public String changeLocal(String language) {
String[] s = language.split("_");
LocaleContextHolder.setLocale(new Locale(s[0], s[1]));
RspWrapper res = new RspWrapper<>().setCode(200).setMsg(MsgUtil.get("200")).setData(true);
return JSON.toJSONString(res);
}
Copy the code
As shown below, even if the language is changed, the default Chinese is returned
The solution is to specify true as the second inheritable parameter when setting the Locale
@GetMapping(path = "change3")
@ResponseBody
public String changeLocal(String language) {
String[] s = language.split("_");
LocaleContextHolder.setLocale(new Locale(s[0], s[1]));
RspWrapper res = new RspWrapper<>().setCode(200).setMsg(MsgUtil.get("200")).setData(true);
return JSON.toJSONString(res);
}
Copy the code
4. Cache international information through Cookies
The parameter language=zh_CN needs to be passed each time. We also need to parse the request parameter ourselves. We can consider using interceptors to achieve uniform Local Settings
The interceptor can be written in the same way as above, but it is recommended to use the wrapper
@Configuration
public class AutoConfig implements WebMvcConfigurer {
/ * * * if this does not exist, would be thrown exception: nested exception is Java. Lang. UnsupportedOperationException: Cannot change HTTP accept header - use a different locale resolution strategy * *@return* /
@Bean
public LocaleResolver localeResolver(a) {
// SessionLocalResolver can also be used, depending on the scope of internationalization
CookieLocaleResolver localeResolver = new CookieLocaleResolver();
localeResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
return localeResolver;
}
/** * Set the localization ** according to the request parameters@return* /
@Bean
public LocaleChangeInterceptor localeChangeInterceptor(a) {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
// Defaults to "locale" if not set
localeChangeInterceptor.setParamName("language");
return localeChangeInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry interceptorRegistry) { interceptorRegistry.addInterceptor(localeChangeInterceptor()); }}Copy the code
Please note that the above localResolver, when we don’t register this bean, run will throw an exception nested exception is Java. Lang. UnsupportedOperationException: Cannot change HTTP accept header – use a different locale resolution
In the example above, CookieLocaleResolver is used, so the language information will be cached in the cookie. Once changed, the subsequent changes will take effect
Test the following
@GetMapping(path = "say")
@ResponseBody
public String say(String name) {
RspWrapper res = new RspWrapper<>().setCode(200).setMsg(MsgUtil.get("200")).setData(MsgUtil.get("name") + ":" + name);
return JSON.toJSONString(res);
}
@GetMapping(path = "say2")
@ResponseBody
public String say2(String name) {
RspWrapper res = new RspWrapper<>().setCode(200).setMsg(MsgUtil.get("200")).setData(MsgUtil.get("name") + ":" + name);
return JSON.toJSONString(res);
}
Copy the code
The main place to set the language, subsequent access without language parameters, will reuse the previous set of language, so that the use of more concise
5. Internationalization of page elements
The json string returned above supports internationalization. Another scenario is the page we return. We hope that the rendered data can also achieve internationalization support
How difficult is it to implement this on the basis of the above
In the resources directory, create the templates directory and create the template file index.html
<! DOCTYPEhtml>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="YiHui"/>
<meta name="viewport" content="Width = device - width, initial - scale = 1.0"/>
<title>A gray blog internationalization test page</title>
</head>
<body>
<div>
<div class="title">hello world!</div>
<br/>
<div class="content" th:text="'name: ' + ${name}">Default Username</div>
<br/>
<div class="sign" th:text="'pwd: ' + ${pwd}">The default password</div>
<br/>
</div>
</body>
</html>
Copy the code
The corresponding controller
@GetMapping(path = {"", "/", "/index"})
public String index(Model model) {
model.addAttribute("name", MsgUtil.get("name"));
model.addAttribute("pwd", MsgUtil.get("pwd"));
return "index";
}
Copy the code
Although the above implementation of nationalization support, but it does not look very elegant, do you need to escape the backend interface, there is no easier way?
Themeleaf provides simpler support by changing the $to #
<! DOCTYPEhtml>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="YiHui"/>
<meta name="viewport" content="Width = device - width, initial - scale = 1.0"/>
<title>A gray blog internationalization test page</title>
</head>
<body>
<div>
<div class="title">hello world!</div>
<br/>
<div class="content" th:text="'name: ' + #{name}">Default Username</div>
<br/>
<div class="sign" th:text="'pwd: ' + #{pwd}">The default password</div>
<br/>
<div class="content" th:text="' 200 - '+ # {200}">200</div>
<br/>
<div class="content" th:text="' 500 - '+ # {500}">500</div>
</div>
</body>
</html>
Copy the code
Corresponding to the rest of
@GetMapping(path = "show")
public String show(a) {
return "show";
}
Copy the code
6. Precautions
In the process of achieving internationalization, we encountered the following problems, which are hereby recorded
6.1 Configuration Information Cannot Be Obtained
In the use of messageSource. GetMessage (msgKey, null, LocaleContextHolder getLocale ()) query configuration information, Results suggest org. Springframework. Context. NoSuchMessageException: No message found under code ‘200’ for the locale ‘en_US’.
Basename = spring.messages.basename = value = directory + language = prefix
- For example, my configuration file is
i18n/messages/messages_en_US.properties
, then the value should bei18n/messages/messages
6.2 Chinese garbled characters
- Set the coding
spring.messages.encoding=utf-8
If the above Settings do not take effect, consider whether the configuration file is utF-8 encoded
6.3 Support internationalization on request
You need to add a LocaleChangeInterceptor to resolve the locale according to the request parameters
Second, register LocaleResolver, such as CookieLocaleResolver used in demo, to store internationalization information (if not set it will throw exceptions)
II. The other
0. Project
- Project: github.com/liuyueyi/sp…
- Project source: github.com/liuyueyi/sp…
1. An ashy Blog
As far as the letter is not as good, the above content is purely one’s opinion, due to the limited personal ability, it is inevitable that there are omissions and mistakes, if you find bugs or have better suggestions, welcome criticism and correction, don’t hesitate to appreciate
Below a gray personal blog, record all the study and work of the blog, welcome everyone to go to stroll
- A grey Blog Personal Blog blog.hhui.top
- A Grey Blog-Spring feature Blog Spring.hhui.top