spring's road to becoming God Chapter 26: detailed explanation of internationalization

Posted by elearnindia on Sat, 30 Oct 2021 21:25:34 +0200

Interview with an e-commerce company two days ago:

Interviewer: have you ever used Spring's internationalization in China? Can you introduce it?

Me: the internationalization support in spring is very good. It's relatively simple. You only need to configure several properties files according to the language, and then mainly register an internationalization related bean. At the same time, you need to specify the location of the configuration file. Basically, it's OK

Interviewer: what if the content of the configuration file changes? How did you solve it?

Me: ah, there is an implementation class for spring internationalization, which can detect changes in configuration files and solve your problem

Interviewer: can we manage these internationalized configurations in db?

Me: I haven't done this place. Basically, we put the internationalized configuration file in the properties file of the project; However, according to my understanding of spring, the extension of spring is very excellent. It should be possible to do so. Just implement the spring internationalization related interfaces yourself.

Interviewer: what is the salary expectation?

Me: 20000

Interviewer: congratulations on coming to work next week!

For your convenience, I'm going to refine this knowledge for your interview and use.

This question

  1. How to use the internationalization of Spring in China?

  2. How does internationalization deal with the change of resource files?

  3. How to implement international resource allocation in db?

First, what is internationalization

Simple understanding is to make different responses to different languages.

For example, the page has a form for filling in user information and a name input box

You can select a language in the browser

When Chinese is selected, it will display:

Name: an input box

When English is selected, it will display:

Full name: An input box

Internationalization does this by displaying different information in different languages.

Therefore, to support internationalization, you must first know which region and language you choose. java.util.Locale is used in java to represent the object of regional language, which contains the information of country and language.

There is a common construction method in Locale

public Locale(String language, String country) {
    this(language, country, "");
}

2 parameters:

language: language

country: country

The values of language and country parameters are not scrawled, and there are unified international standards:

For example, the value of language: zh stands for Chinese, en stands for English, and Chinese may be used in many regions, such as: CN in mainland China and SG in Singapore; English is also used in many countries. GB means Britain and CA means Canada

Abbreviation format of national language: language country, such as zh CN (Chinese [China]), Zh SG (Chinese [Singapore]), en GB (English [UK]),

EN Ca (English [Canada]).

There are many more. I won't elaborate here. The national language code provides you with a table: http://www.itsoku.com/article/282

Many common Locale objects have been created in the Locale class. You can take them directly and use them. Just list a few:

static public final Locale SIMPLIFIED_CHINESE = createConstant("zh""CN"); //zh_CN
static public final Locale UK = createConstant("en""GB"); //en_GB
static public final Locale US = createConstant("en""US"); //en_US
static public final Locale CANADA = createConstant("en""CA"); //en_CA

Looking back at the previous question: the tag corresponding to the name is displayed on the page. We need to obtain the corresponding internationalization information according to a key and Locale information. spring provides the implementation of this part. Let's see the details below.

How to use the internationalization of Spring in China?

MessageSource interface

spring internationalization is supported through the MessageSource interface

org.springframework.context.MessageSource

There are three commonly used methods to obtain internationalization information. Let's take a look

public interface MessageSource {

    /**
     * Get internationalization information
     * @param code Represents the attribute name in the internationalized resource;
     * @param args Used to pass the operation parameters used for formatting string placeholders;
     * @param defaultMessage When the corresponding attribute name cannot be found in the resource, the default information specified by the defaultMessage parameter is returned;
     * @param locale Represents a localized object
     */

    @Nullable
    String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);

    /**
     * It is similar to the above method, except that NoSuchMessageException is thrown directly when the corresponding property name in the resource is not found
     */

    String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;

    /**
     * @param MessageSourceResolvable Encapsulate the attribute name, parameter array and default information, and its function is the same as that of the first method
     */

    String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;

}

Three common implementation classes

ResourceBundleMessageSource

This is a Java based implementation of the ResourceBundle basic class, which allows international resources to be loaded only through the resource name

ReloadableResourceBundleMessageSource

This function is similar to the function of the first class. It has a timed refresh function, which allows you to update resource information without restarting the system

StaticMessageSource

It allows us to provide internationalization information programmatically. Later, we can use this to realize the function of storing internationalization information in db.

Three steps to using internationalization in Spring

Generally, when we use spring, we will use spring containers with ApplicationContext. These containers generally inherit the AbstractApplicationContext interface, which implements the internationalization interface MessageSource mentioned above. Therefore, the ApplicationContext type containers we usually use have their own internationalization function.

Generally, we use internationalization in containers of ApplicationContext type in three steps

Step 1: create an internationalization file

Step 2: register a bean of MessageSource type in the container. The bean name must be MessageSource

Step 3: call getMessage in AbstractApplicationContext to obtain internationalization information, which will be internally handed over to the bean with messageSource name registered in step 2 for processing

Take a case and feel it

Create internationalization file

International file naming format: name_ Language_ Region.properties

Let's three files, all of which are placed in the following directory

com/javacode2018/lesson002/demo19/
message.properties
name=Your name
personal_introduction=Default profile:{0},{1}

This file name does not specify Local information. When the system cannot find it, this default file name will be used

message_zh_CN.properties: Chinese [China]
name=full name
personal_introduction=Personal introduction:{0},{1},{0}
message_en_GB.properties: English [UK]
name=Full name
personal_introduction=personal_introduction:{0},{1},{0}

Registering internationalized bean s in spring

Note that it must be of MessageSource type, and the bean name must be MessageSource. Here we use the ResourceBundleMessageSource class

package com.javacode2018.lesson002.test19.demo1;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;

@Configuration
public class MainConfig1 {
    @Bean
    public ResourceBundleMessageSource messageSource() {
        ResourceBundleMessageSource result = new ResourceBundleMessageSource();
        //You can specify the location of the internationalization configuration file in the format of path / file name,Note that [language] is not included_country.properties]Including this part
        result.setBasenames("com/javacode2018/lesson002/demo19/message"); //@1
        return result;
    }
}

@1: Note the writing method of this place. You can specify the location and format of internationalization configuration file: path / file name. Note that [language country. properties] is not included

Let's have a test case

package com.javacode2018.lesson002.test19;

import com.javacode2018.lesson002.test19.demo1.MainConfig1;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.Locale;

public class MessageSourceTest {

    @Test
    public void test1() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(MainConfig1.class);
        context.refresh();
        //If no locale is specified, the system will take the default locale object. The default local value is Chinese [China], that is, zh_CN
        System.out.println(context.getMessage("name"nullnull));
        System.out.println(context.getMessage("name"null, Locale.CHINA)); //CHINA Correspondence: zh_CN
        System.out.println(context.getMessage("name"null, Locale.UK)); //UK corresponds to en_GB
    }
}

Run output

Your name
Your name
Full name

If no locale is specified in the first line, the system will take the default locale object. The default value of the local is Chinese [China], that is, zh_CN. Therefore, the content in message_zh_CN.properties will be obtained.

In the next two lines, the Locale object is specified. Find the corresponding internationalization file and take the value.

Dynamic parameter usage

Note the personal in the configuration file_ Introduction, personal introduction, is quite special. It includes {0}, {1}, {0}. This is the dynamic parameter. When calling getMessage, pass it through the second parameter to see the usage:

@Test
public void test2() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(MainConfig1.class);
    context.refresh();
    //If Locale is not specified, the system will take the default value of the local computer, Chinese [China], that is: zh_CN
    System.out.println(context.getMessage("personal_introduction"new String[]{"spring killer""java killer"}, Locale.CHINA)); //CHINA Correspondence: zh_CN
    System.out.println(context.getMessage("personal_introduction"new String[]{"spring""java"}, Locale.UK)); //UK corresponds to en_GB
}

Run output

Default profile:spring killer,java killer
personal_introduction:spring,java,spring

Monitor changes in international documents

The function of ReloadableResourceBundleMessageSource is similar to that of ResourceBundleMessageSource in the above case, but there are several functions that can monitor the changes of international resource files. There is a method to set the cache time:

public void setCacheMillis(long cacheMillis)

-1: Indicates forever cache

0: each time the internationalization information is obtained, the internationalization file will be re read

Greater than 0: the last time the configuration file was read exceeds the current time. Reread the internationalization file

There is also a method to set the cache time by seconds, setCacheSeconds, which is similar to setCacheMillis

Let's take a case

package com.javacode2018.lesson002.test19.demo2;

import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;

@Configuration
public class MainConfig2 {
    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource result = new ReloadableResourceBundleMessageSource();
        result.setBasenames("com/javacode2018/lesson002/demo19/message");
        //Set cache time 1000 ms
        result.setCacheMillis(1000);
        return result;
    }
}

message_ zh_ Add a new line in cn.properties

address=Shanghai

Corresponding test cases

@Test
public void test3() throws InterruptedException {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(MainConfig2.class);
    context.refresh();
    //Output twice
    for (int i = 0; i < 2; i++) {
        System.out.println(context.getMessage("address"null, Locale.CHINA));
        TimeUnit.SECONDS.sleep(5);
    }
}

There is a loop above. After the first output, modify the message_ zh_ The address in cn.properties is Shanghai Songjiang, and the final operation results are as follows:

Shanghai
Shanghai Songjiang

Note: in the online environment, it is better to set the cache time a little larger, and the performance will be better.

Internationalization information exists in db

Above, we introduced a class: StaticMessageSource, which allows us to provide internationalization information programmatically. Through this class, we can obtain internationalization information from db.

There are two important methods in this class:

public void addMessage(String code, Locale locale, String msg);
public void addMessages(Map<String, String> messages, Locale locale);

These two methods are used to add internationalization configuration information.

Let's look at the case

Customize a StaticMessageSource class

package com.javacode2018.lesson002.test19.demo3;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.support.StaticMessageSource;

import java.util.Locale;

public class MessageSourceFromDb extends StaticMessageSource implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        //Here, after the current bean is initialized, we simulate the acquisition of international information from db, and then call addMessage to configure the internationalization information.
        this.addMessage("desc", Locale.CHINA, "I'm from db Information from");
        this.addMessage("desc", Locale.UK, "MessageSource From Db");
    }
}

The above class implements the InitializingBean interface of spring and rewrites the afterPropertiesSet method in the interface. This method will be called after the current bean initialization. In this method, the internationalization information is simulated from db, then addMessage is used to configure the internationalization information.

To a spring configuration class, register MessageSourceFromDb to the spring container

package com.javacode2018.lesson002.test19.demo3;

import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MainConfig3 {
    @Bean
    public MessageSource messageSource(){
        return new MessageSourceFromDb();
    }
}

Upper test case

@Test
public void test4() throws InterruptedException {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(MainConfig3.class);
    context.refresh();
    System.out.println(context.getMessage("desc"null, Locale.CHINA));
    System.out.println(context.getMessage("desc"null, Locale.UK));
}

Run output

I'm from db Information from
MessageSource From Db

Why must the bean name be messageSource

Above, I will call the refresh method when the container is started. The process is as follows:

org.springframework.context.support.AbstractApplicationContext#refresh
Internal call
org.springframework.context.support.AbstractApplicationContext#initMessageSource
This method is used to initialize MessageSource,Method will find out whether there is any in the current container messageSource Nominal bean,If so, treat it as the object of internationalization
If it is not found, a file named messageSource of MessageSource

Using internationalization in custom bean s

If you want to use internationalization for custom bean s, it is relatively simple. You only need to implement the following interface. The spring container will automatically call this method to inject MessageSource, and then we can use MessageSource to obtain internationalization information.

public interface MessageSourceAware extends Aware {
    void setMessageSource(MessageSource messageSource);
}

summary

This paper introduces the use of internationalization, involving the Locale class in java. This class is used to represent language country information. This parameter needs to be carried when obtaining internationalization information. In spring, the internationalization function is supported through the MessageSource interface. There are three common implementation classes to understand. StaticMessageSource supports hard coding to configure internationalization information.

If you need spring to support internationalization, you need to register a MessageSource with the bean name MessageSource. You must pay attention to this.

Here, we can easily deal with the above three interview questions.

Case source code

https://gitee.com/javacode2018/spring-series

All the case codes of passerby a java will be put on this in the future. Let's watch it and continue to pay attention to the dynamics.

Source: https://mp.weixin.qq.com/s?__ biz=MzA5MTkxMDQ4MQ==&mid=2648934484&idx=1&sn=ef0a704c891f318a7c23fe000d9003d5&chksm=8862106abf15997c39a3387ce7b2e044cfb3abd92b908eb0971d084c8238ff5f99af412d6054&token=1299257585&lang=zh_ CN&scene=21#wechat_ redirect