web项目 crud 二 国际化

  • 下面以项目的首页为例进行说明

准备国际化文件

  • 编写国际化配置文件,抽取页面需要显示的国际化消息
  • 在类路径下新建一个目录“i18n”(目录名字随意)
  • 然后在“i18n”目录下新建 “index.properties”、“index_en_US.properties”、“index_zh_CN.properties” 三个属性文件
  • “index.properties”:存放默认的语言,名称通常与页面名称一致
  • “index_en_US.properties”:存放英文,en表示语言(英文)缩写,US表示国家(美国)缩写,请按规范格式写即可
  • “index_zh_CN.properties”:存放中文,zh表示语言(中文)缩写,CN表示国家(中国)缩写,请按规范格式写即可
  • 当然还可以写更多的语言文件,当它们的命名符合规范时,IDEA会自动将它们绑定到一起,这样在后面输入值的时候会很方便

index.properties

login_button=登入
login_name=用户名
login_password=密码
login_remember=记住我
login_tip=请登入

index_en_US.properties

login_button=Sign in
login_name=Username
login_password=Password
login_remember=Remember Me
login_tip=Please Sign In

index_zh_CN.properties

login_button=登入
login_name=用户名
login_password=密码
login_remember=记住我
login_tip=请登入
  • 为了防止properties资源文件中文乱码,请勾选如下IDEA设置

Spring Boot 自动配置国际化原理

  • 以前使用 Spring MVC 来做国际化的步骤如下:
  1. 编写国际化配置文件,这一步 Spring Boot 同样要做,上面已经做了
  2. 使用 ResourceBundleMessageSource API管理国际化资源文件,Spring Boot帮我们做了
  3. 在页面使用 fmt:message 取出国际化内容,Spring Boot 因为这里使用 Thymeleaf 模板引擎,所以会用 Thymeleaf 进行取值
  • Spring Boot 现在自动配置好了管理国际化资源文件的组件 MessageSourceAutoConfiguration,可以看出底层仍然使用的是 ResourceBundleMessageSource;
     
package org.springframework.boot.autoconfigure.context;
@Configuration
@ConditionalOnMissingBean(value = MessageSource.class, search = SearchStrategy.CURRENT)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Conditional(ResourceBundleCondition.class)
@EnableConfigurationProperties
public class MessageSourceAutoConfiguration {
 
	private static final Resource[] NO_RESOURCES = {};
 
	@Bean
	@ConfigurationProperties(prefix = "spring.messages")
	public MessageSourceProperties messageSourceProperties() {
		return new MessageSourceProperties();
	}
 
	@Bean
	public MessageSource messageSource() {
		MessageSourceProperties properties = messageSourceProperties();
		ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
		if (StringUtils.hasText(properties.getBasename())) {
			messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(
					StringUtils.trimAllWhitespace(properties.getBasename())));
		}
		if (properties.getEncoding() != null) {
			messageSource.setDefaultEncoding(properties.getEncoding().name());
		}
		messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
		Duration cacheDuration = properties.getCacheDuration();
		if (cacheDuration != null) {
			messageSource.setCacheMillis(cacheDuration.toMillis());
		}
		messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
		messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
		return messageSource;
	}
......
  • 如上所示在它的 MessageSourceProperties 可以看到可以配置的属性,同理可以直接在官网进行查看,可以在全局配置文件中覆盖其默认配置,官方文档查看:https://docs.spring.io/spring-boot/docs/2.0.3.RELEASE/reference/htmlsingle/”
     
# INTERNATIONALIZATION (MessageSourceProperties)
spring.messages.always-use-message-format=false # Whether to always apply the MessageFormat rules, parsing even messages without arguments.
spring.messages.basename=messages # Comma-separated list of basenames (essentially a fully-qualified classpath location), each following the ResourceBundle convention with relaxed support for slash based locations.
spring.messages.cache-duration= # Loaded resource bundle files cache duration. When not set, bundles are cached forever. If a duration suffix is not specified, seconds will be used.
spring.messages.encoding=UTF-8 # Message bundles encoding.
spring.messages.fallback-to-system-locale=true # Whether to fall back to the system Locale if no files for a specific Locale have been found.
spring.messages.use-code-as-default-message=false # Whether to use the message code as the default message instead of throwing a "NoSuchMessageException". Recommended during development only.
  • 如上所示,spring.messages.basename=messages,就是配置国际化资源文件的基本名路径,默认是“message”,意思是如果我们没有在全局配置文件中覆盖此配置项的话,它默认会扫描类路径下的“messages.properties”、"messages_en_US.properties"、"messages_zh_CN.properties"等格式的文件作为国际化文件
  • 因为应用会有多个页面,就会有多套资源文件,这时 spring.messages.basename 的值用逗号分隔即可

下面重新修改之前的index.html页面,为它获取国际化值

<!DOCTYPE html>
<html  lang="en"  xmlns:th="http://www.thymeleaf.org">
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
		<meta name="description" content="">
		<meta name="author" content="">
		<title>Signin Template for Bootstrap</title>
		<!-- Bootstrap core CSS -->
		<link href="asserts/css/bootstrap.min.css" rel="stylesheet">
		<!-- Custom styles for this template -->
		<link  th:href="@{asserts/css/signin.css} " rel="stylesheet">
	</head>

	<body class="text-center">
		<form class="form-signin" action="dashboard.html">
			<img class="mb-4" src="asserts/img/bootstrap-solid.svg" alt="" width="72" height="72">
			<h1 class="h3 mb-3 font-weight-normal" th:text="#{login_tip}">Please sign in</h1>
			<label class="sr-only">Username</label>
			<input type="text" class="form-control" th:placeholder="#{login_name}" required="" autofocus="" >
			<label class="sr-only">Password</label>
			<input type="password" class="form-control"  th:placeholder="#{login_password}" required="">
			<div class="checkbox mb-3">
				<label>
          <input type="checkbox" th:value="#{login_remember}"> [[#{login_remember}]]
        </label>
			</div>
			<button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login_button}">Sign in</button>
			<p class="mt-5 mb-3 text-muted">© 2017-2018</p>
			<a class="btn btn-sm" th:href="@{/(l=zh_CN)}">中文</a>
			<a class="btn btn-sm" th:href="@{/(l=en_US)}">English</a>
		</form>
	</body>
</html>
  • th:text="#{xxx}":获取国际化文件中的xxx属性值,然后替换标签原来的文本默认值
  • th:placeholder="#{xxx}":获取国际化文件中的xxx属性值,然后替换标签placeholder属性原来的默认值,同理可以替换任意属性的默认值
  • [[#{login_remember}]]:对于 <input xxx />这种自封闭标签得用 Thymeleaf的行内表达式取值

  • 效果:根据浏览器语言设置的信息切换了国际化;

指定语言切换

  • 上面是应用根据跟随浏览器语音自动进行语音切换,现在需要改成用户“点击中文”则切换中文,“点击英文”则切换英文。
  • 原理是需要使用到 “国际化Locale(区域信息对象)”与 “ LocaleResolver(获取区域信息对象)”两个API 

切换原理

  • 应用国际化默认之所以能随着浏览器的语言变化而变化,是因为浏览器Http请求时,会在请求头中携带语言信息,这是后台国际化变换的依据。

  • 第一部分中的 “自动语言切换” 之所以能用,是因为在 WebMvcAutoConfiguration 配置类中自动配置好了一个 区域信息解析器组件,如下所示逻辑是如果用户没有自己提供 区域信息解析器 “LocaleResolver”,那么它默认会使用一个叫 “AcceptHeaderLocaleResolver” 进行解析
  • 所以我们只需要自己提供一个  “LocaleResolver” 组件覆盖默认的即可
    package org.springframework.boot.autoconfigure.web.servlet;
    .....
    @Configuration
    @ConditionalOnWebApplication(type = Type.SERVLET)
    @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
    @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
    @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
    		ValidationAutoConfiguration.class })
    public class WebMvcAutoConfiguration {
    ......
    		@Bean
    		@ConditionalOnMissingBean
    		@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
    		public LocaleResolver localeResolver() {
    			if (this.mvcProperties
    					.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
    				return new FixedLocaleResolver(this.mvcProperties.getLocale());
    			}
    			AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
    			localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
    			return localeResolver;
    		}
    .......
    }
    

    index.html 页面修改

  • 修改一下首页的“中文”、“英文”按钮,使用 Thymeleaf 的 @{xxx} 取值是会默认带上应用上下文的
  • @{/} 的效果就是 http://localhost:8080/tiger/
  • @{/(l=zh_CN)} 相当于 @{/?zh_CN},只是 Thymeleaf 不再使用 "?" 的方式为get请求路径带参数,而是使用"()"的方式
  • 页面上点击“中文”后,会跳转:http://localhost:8080/tiger/?l=zh_CN
  • 页面上点击“英文”后,会跳转:http://localhost:8080/tiger/?l=en_US
        <div class="checkbox mb-3">
            <label>
                <input type="checkbox" value="remember-me"/> [[#{login_remember}]]
            </label>
        </div>
        <button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login_button}">Sign in</button>
        <p class="mt-5 mb-3 text-muted">© 2017-2018</p>
        <a class="btn btn-sm" th:href="@{/(l=zh_CN)}">中文</a>
        <a class="btn btn-sm" th:href="@{/(l=en_US)}">English</a>
    </form>
    </body>
    </html>
    

    自定义区域信息解析器

/**
 * Copyright (C), 2015-2019, XXX有限公司
 * FileName: MyLocaleResolve
 * Author:   Administrator
 * Date:     2019/2/26 19:28
 * Description:
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package com.xuxu.component;

import org.springframework.util.StringUtils;
import org.springframework.web.servlet.LocaleResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;

/**
 * 〈一句话功能简述〉<br> 
 * 〈〉
 *
 * @author Administrator
 * @create 2019/2/26
 * @since 1.0.0
 */
public class MyLocaleResolve  implements LocaleResolver {


    @Override
    public Locale resolveLocale(HttpServletRequest request) {
            /**
             * 如果请求地址中含有l参数,如 http://localhost:8080/tiger/?l=en_US,则设置新的区域对象
             * 否则使用默认的区域对象,默认的区域对象不会使国际化再随着浏览器语言的变化而变化的
             */
            Locale locale = Locale.getDefault();
            String l = request.getParameter("l");
            if (!StringUtils.isEmpty(l)) {
                String[] ls = l.split("_");
                locale = new Locale(ls[0], ls[1]);
            }
            return locale;
    }

    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {

    }
}

添加组件

package com.lct.config;
 
import com.lct.component.MyLocaleResolve;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
 
/**
 * Created by Administrator on 2018/7/28 0028.
 * 自定义配置类
 */
@Configuration
public class MyMvcConfig {
 
    /**
     * 将我们自己的 LocaleResolver 组件交于 Spring 容器管理
     * 从而覆盖 Spring Boot默认的区域信息解析器
     *
     * @return
     */
    @Bean
    public LocaleResolver localeResolver() {
        return new MyLocaleResolve();
    }
 
}

运行测试

  • 和预期的完全一致,因为请求路径中携带了"l"参数,所以不管浏览器 语言 选择的是什么,都不会影响
  • 当请求地址中不带 “l” 参数时,此时后台使用的是默认的 Locale,此时即使浏览器切换了语言,同样国际化也不会生效的

猜你喜欢

转载自blog.csdn.net/baiyan3212/article/details/87923124