Website Internationalization Implementation (2) - Spring MVC Internationalization Implementation and Principle

 

1. Background

Many websites have users all over the world, so websites need to display content in different languages ​​for users in different countries, so there is a need for internationalization. Most websites will set up language switching links at the head or tail of the website, so that You can directly switch to the corresponding content. Some of these sites are differentiated by website addresses or parameters, and some are differentiated by setting cookie values.

 

Second, the solution

I have written an article on JDK internationalization support , explaining the specific implementation of JDK internationalization. So how to implement the internationalization of the website?

 

In fact, the internationalization implementation of the website is similar to the JDK implementation idea introduced earlier, except that the acquisition of localization information needs to be obtained from the page. Once the page information is obtained, the corresponding data is obtained and formatted, and finally rendered to the page. Here we mainly describe the back-end processing ideas. The front-end processing ideas are actually similar, but the implementation methods are different.

 

How to get localization information from the page? This is the first part of all processing, and several commonly used methods are:


(1) Get the localization information directly according to the Request.getLocale() method, in fact, take the value corresponding to "accept-language" from the Http Request Headers, which has the language information on the browser side;
(2) Save it on the browser side A cookie with a custom name, a value is specified by default, and the corresponding switch is modified by clicking the language switch link;
(3) Add a parameter with localized information to the request URL or the address contains localized information.

 

Through the above methods, the localization information can be directly obtained from the request in the web program, and then the data can be obtained from the corresponding properties file according to the localization information (for example, through the ResourceBundle class of the JDK), and if the data is obtained, if necessary Then format the data (for example, through the MessageFormat class of JDK), and finally display the processed data to the foreground to complete the entire internationalization operation.

 

The idea is already there, so how to implement it? The implementation of Spring MVC is taken as an example below, because the framework has done a good abstraction and encapsulation, which is a very good reference example.

 

Three, Spring MVC implementation and principle

3.1 Obtaining localized information

3.1.1 Overview

The DispatcherServlet class of Spring MVC will look for a locale resolver in the initLocaleResolver method, and if not found, it will use the default AcceptHeaderLocaleResolver class. The locale resolver will set the current locale information according to the request request.

In addition to the resolver class, you can also define interceptors to set locale information, such as setting through request parameters, as detailed below.

 

Spring MVC-related processing classes are all under the org.springframework.web.servlet.i18n package. The localization information can be obtained through the RequestContext.getLocale() method. In addition, the RequestContext.getTimeZone() method can also get time zone information.

 

3.1.2 AcceptHeaderLocaleResolver

This can also be seen from the name. This class is the accept-language value in the header of the parsing request. This value usually contains the localization information supported by the client, so the localization information can be obtained through this value. However, this class cannot get time zone information. This class is configured by default, so no additional configuration is required to use it.

 

3.1.3 CookieLocaleResolver

This class is used to access localized information through cookies. The client can store a value with a specified name in the cookie to represent the localized information, and then the class can be obtained and parsed accordingly. The specific configuration is as follows:

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
 <property name="cookieName" value="clientlanguage"/>
 <property name="cookieMaxAge" value="100000"/>
 <property name="cookiePath" value="/"/>
</bean>

 
Here is a description of several configuration properties:

 

     
cookieName Default: classname + LOCALE cookie name
cookieMaxAge Default: Servlet container default This value is how long the cookie is kept on the client side. If the value is -1, it will not be retained; this value will be invalid after closing the browser.
cookiePath Defaults:/ This value sets the applicable path of the cookie. If this value is set, it means that the cookie is only visible to the current directory and its subdirectories.

 

3.1.4 SessionLocaleResolver

This class obtains localized information through request, and then stores it in HttpSession, so localized information access depends on the life cycle of the session. The specific configuration is as follows:

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
</bean>
 

 

3.1.5 LocaleChangeInterceptor

This interceptor will intercept the parameters in the request, and then call the setLocale() method of LocaleResolver according to the parameters to change the current locale value. Here is an example, there is this address http://www.sf.net/home.view?siteLanguage=nl , the parameter siteLanguage represents the locale information, configure the interception to modify the locale value:

<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
 <property name="paramName" value="siteLanguage"/></bean>
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"/>
 

CookieLocaleResolver is configured here because LocaleChangeInterceptor needs to call the setLocale() method of LocaleResolver. This example uses CookieLocaleResolver. Of course, other LocaleResolver implementation classes can also be used.

 

3.2 Data acquisition and formatting

The data processing of Spring MVC defines an interface MessageSource, which defines the method of data acquisition. Methods as below:

 

  1. String getMessage(String code, Object[] args, String defaultMessage, Locale locale);
    here code is the key value in the properties file, args is the parameter value to be replaced in the file, defaultMessage is the default content when the content is not found, locale for localized information.
  2. String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException;
    This method is similar to the above method, except that there is no default content, but an exception is thrown when the content is not found.
  3. String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
    there is a new interface MessageSourceResolvable in the parameters, which encapsulates the previous parameters, and locale is the localization information.

For the MessageSource interface, Spring MVC's ApplicationContext and HierarchicalMessageSource both inherit. When the ApplicationContext is loaded, it will first go to the context to find the implementation of the bean named messageSource, and then use this implementation class for the invocation of the above MessageSource method; When you arrive, you will find the class containing the MessageSource bean to use; if you can't find it again, you will use DelegatingMessageSource to perform method calls.

 

The common implementations of MessageSource are as follows:

 

  1. ResourceBundleMessageSource class: This class actually depends on the JDK's ResourceBundle class to obtain data and MessageFormat to format it.
  2. ReloadableResourceBundleMessageSource class: This is more reloadable than the above, that is, new content can be re-read without restarting the application. There are also differences in the specific implementation methods. This class is loaded through Spring's PropertiesPersister strategy and depends on the JDK's Properties class to read the content.
  3. StaticMessageSource class: This class provides a simple implementation, and the content needs to be configured first. Use less, suitable for use in less content and simpler situations.

The following is a simple example of the most commonly used ResourceBundleMessageSource class:

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
 <property name="basenames">
 <list>
 <value>format</value>
 <value>exceptions</value>
 </list>
 </property>
 </bean>

 
The format and exceptions are the file base names, and the corresponding content can be configured in the corresponding file name of the specific locale. See the following section for detailed examples.

 

3.3 Summary and Examples

The above two sections mainly talk about localization information acquisition, data acquisition and formatting. These two parts are actually the two core parts of the entire internationalization process. As for the matching and receiving of requests, the page rendering of the returned results is not the same. To expand, it is not directly related to internationalization and belongs to the basic content of Spring MVC.

 

Here is a general overview of the entire Spring MVC internationalization process. The whole process is roughly like this: Receive requests -> LocaleResolver gets/set locale information -> MessageSource obtains data and formats -> Content is displayed on the page.

 

After talking for a long time, it is still a bit abstract. Here is a detailed example:

@Controller
public class I18nController {
 @Autowired
 private MessageSource messageSource;
 @RequestMapping("i18n")
 public String i18n(Model model){
 //Get localization information, get it from LocaleContext
 Locale locale = LocaleContextHolder.getLocale ();
 //Initialization parameters, here is a simple demonstration, the real parameters may be processed from the database query. The parameters here correspond to the content that needs to be replaced in the configuration file in the i18n directory
 Object [] objArr = new Object[4];
 objArr[0] = new Date();
 objArr[1] = messageSource.getMessage("goods", null, locale);//This specific item is read from the configuration
 objArr[2] = "taobao";
 objArr[3] = new BigDecimal("39.20");
 //Get the formatted content
 String content = messageSource.getMessage("template", objArr, locale);
 model.addAttribute("content", content);
 return "/i18n/show";
 }
}

 

LocaleResolver configuration, here takes Cookie as an example:

<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
 <property name="cookieName" value="clientlanguage"/>
 <property name="cookieMaxAge" value="100000"/>
 <property name="cookiePath" value="/"/>
</bean>

 

MessageSource configuration, here takes ResourceBundleMessageSource as an example:

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
 <property name="basenames">
 <list>
 <value>/i18n/message</value>
 </list>
 </property>
 </bean>

 

Properties configuration, which is uniformly placed in the /i18n directory, and the message name starts with:


file configuration.png

 

 

More detailed code can be viewed in my Github project .

 

Fourth, the expansion introduction

4.1 How is the Bean corresponding to LocaleResolver initialized?

The initialization work is performed by calling the initLocaleResolver method when the DispatcherServlet class is initialized.

private void initLocaleResolver(ApplicationContext context) {
 try {
 this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
 if (logger.isDebugEnabled()) {
 logger.debug("Using LocaleResolver [" + this.localeResolver + "]");
 }
 }
 catch (NoSuchBeanDefinitionException ex) {
 // We need to use the default.
 this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
 if (logger.isDebugEnabled()) {
 logger.debug("Unable to locate LocaleResolver with name '" + LOCALE_RESOLVER_BEAN_NAME +
 "': using default [" + this.localeResolver + "]");
 }
 }
 }

 
It can be seen from the code that the processing process is divided into two steps:

(1) First take the bean named localeResolver from the current context;

(2) If you can't find it, take the bean with the class name LocaleResolver according to the default strategy, that is, execute the getDefaultStrategy method, which actually takes the value corresponding to org.springframework.web.servlet.LocaleResolver in the DispatcherServlet.properties file, that is The default AcceptHeaderLocaleResolver class, and then create the corresponding bean.


So if the LocaleResolver is customized in the context, the custom one will be used, and the default AcceptHeaderLocaleResolver class will be used if it is not defined. This notation is useful when writing common logic and providing multiple strategies.

 

5. References

  1. Spring MVC using locales official tutorial
  2. Spring MVC using MessageSource official tutorial

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326222451&siteId=291194637