精华内容
下载资源
问答
  • 该Demo源码是博文《基于jQuery.i18n.properties 实现前端页面的资源国际化》里面的源码Demo。博文地址:http://blog.csdn.net/aixiaoyang168/article/details/49336709。 可供下载学习使用。
  • h5国际化demo[i18n实现]

    2016-02-03 17:28:37
    h5国际化demo[i18n实现] 支持多种语言,以及非H5的浏览器
  • SpringMVC实现国际化/多语言项目,具体内容参考http://blog.csdn.net/u013360850/article/details/70860144
  • Springboot 返回数据提示语 国际化 (AOP实现)

    千次阅读 热门讨论 2021-07-22 10:19:25
    提示语的国际化返回,自定义多语言。 本文使用aop方式,拦截接口返回的数据,进行转换。 正文 先看这次示例教学的项目 目录结构: (当然resource里面的i18n文件夹和三个properties文件也是要我们自己建的,...

    前言

    本篇内容:
    提示语的国际化返回,自定义多语言。
    本文使用aop方式,拦截接口返回的数据,进行转换。

    正文

     

    先看这次示例教学的项目 目录结构:

     (当然resource里面的i18n文件夹和三个properties文件也是要我们自己建的,但是 那个Resource Bundle 不用管,这个在yml加上对应配置项自动生成的。 不清楚的继续往下看教学就好)

    开始敲(CV)代码:

    pom.xml 依赖:

        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.68</version>
            </dependency>
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>3.9</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.10</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    

    返回码的枚举

    CodeEnum.java

    /**
     * @author JCccc
     */
    public enum CodeEnum {
    
        SUCCESS(1000, "请求成功"),
        FAIL(2000, "请求失败");
        public final int code;
        public final String msg;
        public Integer getCode() {
            return this.code;
        }
        CodeEnum(int code, String msg) {
            this.code = code;
            this.msg = msg;
        }
        public String getMsg() {
            return this.msg;
        }
    }

    返回数据的简单封装

    ResultData.java 

    import com.test.myi18n.enums.CodeEnum;
    import lombok.Data;
    
    /**
     * @author JCccc
     */
    @Data
    public class ResultData<T> {
        private Integer code;
        private String message;
        private T data;
        public ResultData(int code, String message) {
            this.code = code;
            this.message = message;
        }
        public static ResultData success(CodeEnum codeEnum) {
            return new ResultData(codeEnum.code, codeEnum.msg);
        }
        public static ResultData success(String msg) {
            return new ResultData(CodeEnum.SUCCESS.getCode(),msg);
        }
    }

    Locale、 MessageSource的简单方法封装

    LocaleMessage.java

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.MessageSource;
    import org.springframework.context.i18n.LocaleContextHolder;
    import org.springframework.stereotype.Component;
    
    import java.util.Locale;
    
    /**
     * @author JCccc
     */
    @Component
    public class LocaleMessage {
        @Autowired
        private MessageSource messageSource;
        public String getMessage(String code){
            return this.getMessage(code,new Object[]{});
        }
        public String getMessage(String code,String defaultMessage){
            return this.getMessage(code,null,defaultMessage);
        }
        public String getMessage(String code,String defaultMessage,Locale locale){ return this.getMessage(code,null,defaultMessage,locale); }
        public String getMessage(String code,Locale locale){
            return this.getMessage(code,null,"",locale);
        }
        public String getMessage(String code,Object[] args){ return this.getMessage(code,args,""); }
        public String getMessage(String code,Object[] args,Locale locale){
            return this.getMessage(code,args,"",locale);
        }
        public String getMessage(String code,Object[] args,String defaultMessage){ return this.getMessage(code,args, defaultMessage,LocaleContextHolder.getLocale()); }
        public String getMessage(String code,Object[]args,String defaultMessage,Locale locale){ return messageSource.getMessage(code,args, defaultMessage,locale); }
    }

    i18n语言转换工具类

    I18nUtils.java

    import java.util.Locale;
    import com.test.myi18n.message.LocaleMessage;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    @Component
    public class I18nUtils {
    
        @Autowired
        private LocaleMessage localeMessage;
    
        /**
         * 获取key
         *
         * @param key
         * @return
         */
        public  String getKey(String key) {
            String name = localeMessage.getMessage(key);
            return name;
        }
    
        /**
         * 获取指定哪个配置文件下的key
         *
         * @param key
         * @param local
         * @return
         */
        public  String getKey(String key, Locale local) {
            String name = localeMessage.getMessage(key, local);
            return name;
        }
    }

    接下来是我们转换的一个关键环节, aop方式拦截 controller接口返回的数据:
     

    LanguageAspect.java

    import lombok.AllArgsConstructor;
    import org.apache.commons.lang3.StringUtils;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestAttributes;
    import org.springframework.web.context.request.RequestContextHolder;
    
    import javax.servlet.http.HttpServletRequest;
    import java.util.*;
    
    /**
     * @author JCccc
     */
    @Aspect
    @Component
    @AllArgsConstructor
    @ConditionalOnProperty(prefix = "lang", name = "open", havingValue = "true")
    public class LanguageAspect {
        @Autowired
        I18nUtils i18nUtils;
    
        @Pointcut("execution(* com.test.myi18n.controller.*.*(..)))")
        public void annotationLangCut() {
        }
    
        /**
         * 拦截controller层返回的结果,修改msg字段
         *
         * @param point
         * @param obj
         */
        @AfterReturning(pointcut = "annotationLangCut()", returning = "obj")
        public void around(JoinPoint point, Object obj) {
            Object resultObject = obj;
            try {
                RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
                //从获取RequestAttributes中获取HttpServletRequest的信息
                HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
                String langFlag = request.getHeader("lang");
                if (null != langFlag) {
                    ResultData r = (ResultData) obj;
                    String msg = r.getMessage().trim();
                    if (StringUtils.isNotEmpty(msg)) {
                        if ("CN".equals(langFlag)) {
                            Locale locale = Locale.CHINA;
                            msg = i18nUtils.getKey(msg, locale);
                        } else if ("EN".equals(langFlag)) {
                            Locale locale = Locale.US;
                            msg = i18nUtils.getKey(msg, locale);
                        } else {
                            msg = i18nUtils.getKey(msg);
                        }
                    }
                    r.setMessage(msg);
                }
            } catch (Exception e) {
                e.printStackTrace();
                //返回原值
                obj = resultObject;
            }
        }
    }

    代码简单解读: 

     1.  annotationLangCut 上面切点管控的地址 需要自己改下,改成自己想管控的文件夹路径

     2.  @ConditionalOnProperty 注解,读取yml 里面lang开头的配置项,key为 open ,value 为true

    只有为true,这个aop拦截才会生效

    3.  String langFlag = request.getHeader("lang");
    从这句可以看到我这次文章采取的是让对接接口方(前端)在header里面传入需要使用的语言flag。 例如传入 EN (英文),意思就是需要把提示语转为英文。

    大家可以结合自己的项目实际情况,改为从yml读取或者从数据库读取或者从redis读取等等都可以。

    4.  ResultData r = (ResultData) obj;
         String msg = r.getMessage().trim();

    这两行代码为了就是把拦截到的obj中的message提示语获取出来, 如果大家项目的返回数据不是我文中使用的 ResultData,则需要自己进行魔改调整。

    最后是 三份 mess properties文件:

    mess.properties 

    自定义的返回语= 您好,如果文章对你有用,请关注+收藏+评论

    这个文件按照本文里aop的拦截方式,会先检测 当前 的 语言flag值,如果检测不到就h会到
    mess.properties 文件里面找。
     

    mess_en_US.properties

    请求成功=success
    请求失败=fail

    mess_zh_CN.properties

    请求成功=请求成功
    请求失败=请求失败
    success=请求成功
    fail=请求失败
    

    最后写个测试接口给大家演示一下效果:
     

        @GetMapping("test")
        public ResultData test(@RequestParam int testNum) {
            if (1==testNum){
                return ResultData.success(CodeEnum.SUCCESS);
            }
            if (2==testNum){
                return ResultData.success(CodeEnum.FAIL);
            }
            if (3==testNum){
                return ResultData.success("自定义的返回语");
            }
            return ResultData.success(CodeEnum.SUCCESS);
        }

    调用测试: 

     

    好,就先到这。

    展开全文
  • jquery国际化

    千次阅读 2019-02-13 10:40:55
    近期公司准备重构项目,语言翻译要求不能用映射表,后来发现了jquery国际化(jQuery.i18n.properties ),在前端实现 先简单的一下jQuery.i18n.properties jQuery.i18n.properties是一款轻量级的jQuery国际化插件,...

    近期公司准备重构项目,语言翻译要求不能用映射表,后来发现了jquery国际化(jQuery.i18n.properties ),在前端实现

    先简单的一下jQuery.i18n.properties

    jQuery.i18n.properties是一款轻量级的jQuery国际化插件,能实现Web前端的国际化。

    jQuery.i18n.properties是一款轻量级的jquery国际化插件,能实现Web前端的国际化,国际英文单词为:internationalization,又称i18n,“i"为单词的第一个字母,”18“为”i"和“n”之间的单词个数,而“n"表示单词的最后一个字母。jQuery.i18n.properties采用.properties文件对JavaScript进行国际化,jQuery.i18n.properties插件首先加载默认的资源文件(strings.properties ),然后加载针对特定的语言环境的资源文件(strings_zh.properties ),这节保证了在未提供语言某种语言的翻译时,默认值始终有效

    具体看代码哈哈

    第一步先看一下文件目录

    在这里插入图片描述

    第二步

    • 下载必须的js文件

    第三步

    新建静态页面

    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title class="i18n" name='title'></title>
        <meta id="i18n_pagename" content="index-common">
        <meta name="viewport" content="width=device-width">
        <meta name="keywords" content="" />
        <meta name="description" content=""/>
    </head>
    <body>
        <div class="lan">
            <div class="lan1"><label class="i18n" name="lan"></label></div>
            <div class="lan2">
                <select id="language">
                    <option value="zh-CN">中文简体</option>
                    <option value="zh-TW">中文繁體</option>
                    <option value="en">English</option>
                </select>
            </div>
        </div>
        <br>
        <hr>
        <div><label class="i18n" name="hellomsg1"></label><label class="i18n" name="hellomsg2"></label></div><br>
        <div><label class="i18n" name="commonmsg1"></label><label class="i18n" name="commonmsg2"></label></div><br>
        <div>
            <input type="search" class="i18n-input" selectname="searchPlaceholder" selectattr="placeholder">
        </div>
    
        <script src="jquery-3.3.1.min.js"></script>        
        <!-- 加载语言包文件 -->
        <script src="jquery.i18n.properties.js"></script>
        <script src="language.js"></script>
    </body>
    </html>
    

    说明

    • meta id=”i18n_pagename” content=”index-common” 这里面的index-common写法,这里是引 入了两个资源文件index和common,这样做的好处就是,我们可以把公用部分的资源文件放到common里面,例如页头,页脚等,而且不需在每个页面都复制这部分内容,当共有内容有所变化,只需要修改common.properties就可以全部修改啦。
    • 获取方式一:label class=”i18n” name=”hellomsg1”这里面class=”i18n”写法,下边在js里面我们可以根据类选择器获取需要国际化的地方,然后name=”hellomsg1”这里面的hellomsg1跟资源文件里面的key保持一致。获取方式二:input type=”search” class=”i18n-input” selectname=”searchPlaceholder” selectattr=”placeholder”这里面的class=”i18n-input”写法,跟上边的区别就是可以给html标签的任何属性可以赋值,例如placeholder,name,id什么的都可以,selectattr=”placeholder”里面的placeholder就是要赋值的属性,selectname=”searchPlaceholder”里面的searchPlaceholder跟资源文件里面的key保持一致。

    第四步

    配置language.js

    /**
     * cookie操作
     */
    var getCookie = function(name, value, options) {
      if (typeof value != 'undefined') { // name and value given, set cookie
          options = options || {};
          if (value === null) {
              value = '';
              options.expires = -1;
          }
          var expires = '';
          if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
              var date;
              if (typeof options.expires == 'number') {
                  date = new Date();
                  date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
              } else {
                  date = options.expires;
              }
              expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
          }
          var path = options.path ? '; path=' + options.path : '';
          var domain = options.domain ? '; domain=' + options.domain : '';
          var s = [cookie, expires, path, domain, secure].join('');
          var secure = options.secure ? '; secure' : '';
          var c = [name, '=', encodeURIComponent(value)].join('');
          var cookie = [c, expires, path, domain, secure].join('')
          document.cookie = cookie;
      } else { // only name given, get cookie
          var cookieValue = null;
          if (document.cookie && document.cookie != '') {
              var cookies = document.cookie.split(';');
              for (var i = 0; i < cookies.length; i++) {
                  var cookie = jQuery.trim(cookies[i]);
                  // Does this cookie string begin with the name we want?
                  if (cookie.substring(0, name.length + 1) == (name + '=')) {
                      cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                      break;
                  }
              }
          }
          return cookieValue;
      }
    };
    
    /**
    * 获取浏览器语言类型
    * @return {string} 浏览器国家语言
    */
    var getNavLanguage = function(){
      if(navigator.appName == "Netscape"){
          var navLanguage = navigator.language;
          return navLanguage.substr(0,2);
      }
      return false;
    }
    
    /**
    * 设置语言类型: 默认为中文
    */
    var i18nLanguage = "zh-CN";
    
    /*
    设置一下网站支持的语言种类
    */
    var webLanguage = ['zh-CN', 'zh-TW', 'en'];
    
    /**
    * 执行页面i18n方法
    * @return
    */ 
    var execI18n = function(){
      /*
      获取一下资源文件名
       */
      var optionEle = $("#i18n_pagename");
      if (optionEle.length < 1) {
          console.log("未找到页面名称元素,请在页面写入\n <meta id=\"i18n_pagename\" content=\"页面名(对应语言包的语言文件名)\">");
          return false;
      };
      var sourceName = optionEle.attr('content');
      sourceName = sourceName.split('-');
          /*
          首先获取用户浏览器设备之前选择过的语言类型
           */
          if (getCookie("userLanguage")) {
              i18nLanguage = getCookie("userLanguage");
          } else {
              // 获取浏览器语言
              var navLanguage = getNavLanguage();
              if (navLanguage) {
                  // 判断是否在网站支持语言数组里
                  var charSize = $.inArray(navLanguage, webLanguage);
                  if (charSize > -1) {
                      i18nLanguage = navLanguage;
                      // 存到缓存中
                      getCookie("userLanguage",navLanguage);
                  };
              } else{
                  console.log("not navigator");
                  return false;
              }
          }
          /* 需要引入 i18n 文件*/
          if ($.i18n == undefined) {
              console.log("请引入i18n js 文件")
              return false;
          };
    
          /*
          这里需要进行i18n的翻译
           */
          jQuery.i18n.properties({
              name : sourceName, //资源文件名称
              path : i18nLanguage +'/', //资源文件路径
              mode : 'map', //用Map的方式使用资源文件中的值
              language : i18nLanguage,
              callback : function() {//加载成功后设置显示内容
                  var insertEle = $(".i18n");
                  console.log(".i18n 写入中...");
                  insertEle.each(function() {
                      // 根据i18n元素的 name 获取内容写入
                      $(this).html($.i18n.prop($(this).attr('name')));
                  });
                  console.log("写入完毕");
    
                  console.log(".i18n-input 写入中...");
                  var insertInputEle = $(".i18n-input");
                  insertInputEle.each(function() {
                      var selectAttr = $(this).attr('selectattr');
                      if (!selectAttr) {
                          selectAttr = "value";
                      };
                      $(this).attr(selectAttr, $.i18n.prop($(this).attr('selectname')));
                  });
                  console.log("写入完毕");
              }
          });
    }
    
    /*页面执行加载执行*/
    $(function(){
    
      /*执行I18n翻译*/
      execI18n();
    
      /*将语言选择默认选中缓存中的值*/
      $("#language option[value="+i18nLanguage+"]").attr("selected",true);
    
      /* 选择语言 */
      $("#language").on('change', function() {
          var language = $(this).children('option:selected').val()
          console.log(language);
          getCookie("userLanguage",language,{
              expires: 30,
              path:'/'
          });
          location.reload();
      });
    });
    

    path : i18nLanguage +’/’, //资源文件路径不要搞错啦

    第五步

    新建不同语言的资源文件

    • zh-CN/index.properties

      title=i18n资源国际化
      
      lan=语言选择:
      hellomsg1=首页消息: 
      hellomsg2=资源国际化!这是首页消息!
      searchPlaceholder=请输入搜索信息
      
    • zh-CN/common.properties

      commonmsg1=通用消息: 
      commonmsg2=资源国际化!这是通用消息哦!
      
    • zh-TW/index.properties

      title=i18n資源國際化
      
      lan=語言選擇:
      hellomsg1=首頁消息: 
      hellomsg2=資源國際化! 这是首頁消息!
      searchPlaceholder=請輸入搜索信息
      
    • zh-TW/common.properties

      commonmsg1=通用消息: 
      commonmsg2=資源國際化!這是通用消息哦!
      
    • en/index.properties

      title=i18n Resource Internationalization
      
      lan=Language:
      hellomsg1=Index message: 
      hellomsg2=Hello word! This is index message!
      searchPlaceholder=Please input serach information
      
    • en/common.properties

      commonmsg1=Common message: 
      commonmsg2=This is common message!
      

    注意

    这里我没有按照jquery.i18n.properties上边那种strings.properties,strings_zh.properties写法写,我觉得把资源文件按语言类型文件夹划分,更直观些,故而将所有中文简体放在zh-CN下边,所有中文繁体放在zh-TW下边,英语放在en下边。会导致它每次都会去请求index_zh.properties,出现404请求错误,不过没啥大影响啦!

    还有一点要注意,启动这个项目需要服务器的支持可以用node简单搭建一个,也可以用Apache,有一个不错的工具live-server用npm全局安装一下就好。就相当于一个服务器了,当然现在大部分编辑器都自带服务器想webstorm,vscode,hbuild等

    看一下效果图

    在这里插入图片描述
    在这里插入图片描述
    demo源码

    展开全文
  • java 实现国际化 中英文语言切换

    热门讨论 2013-11-29 11:25:42
    java实现国际化中英文语言切换 java语言切换JSP国际化 java实现国际化中英文语言切换 java语言切换JSP国际化
  • C#(.net)多语言 国际化 本地化示例demo合集
  • springMVC的动态国际化,是使用数据库保存国际化对应的value值,系统启动后加载所有国际化配置到缓存(也可以直接读取数据库,效率低,不推荐),修改数据库中对应的国际化value值后刷新。自定义国际化的解析类,从...

    springMVC的动态国际化,是使用数据库保存国际化对应的value值,系统启动后加载所有国际化配置到缓存(也可以直接读取数据库,效率低,不推荐),修改数据库中对应的国际化value值后刷新。自定义国际化的解析类,从缓存中取得对应的value值

    1.定义国际化对应的实体类,以及对应的mybatis mapper类

    //entry
    public class SysMessageResource implement Serializable {
    	
    	private static final long serialVersionUID = 1L;
    	private String code;		// 键值
    	private String lang;		// 国家区域
    	private String label;		// 显示的标签
    	
    	//....
    	
    }
    
    // mapper
    public class SysMessageResourceMapper {
    
        List<SysMessageResource> findList(SysMessageResource resource);
        // insert
        // update
        // delete
    }

    2.定义SpringContextHolder:

    可以根据class直接获取对应的spring容器管理的对象,(因为我是在Utils的static块中加载的国际化资源,不能使用@autowried注入,当然也可以不用该方法,可以将Utils标为spring对象,继承InitializingBean,在afterPropertiesSet方法中加载国际化资源)

    @Service
    @Lazy(false)
    public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
    
    	private static ApplicationContext applicationContext = null;
    
    	private static Logger logger = LoggerFactory.getLogger(SpringContextHolder.class);
    
    	/**
    	 * 取得存储在静态变量中的ApplicationContext.
    	 */
    	public static ApplicationContext getApplicationContext() {
    		assertContextInjected();
    		return applicationContext;
    	}
    
    	/**
    	 * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
    	 */
    	@SuppressWarnings("unchecked")
    	public static <T> T getBean(String name) {
    		assertContextInjected();
    		return (T) applicationContext.getBean(name);
    	}
    
    	/**
    	 * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
    	 */
    	public static <T> T getBean(Class<T> requiredType) {
    		assertContextInjected();
    		return applicationContext.getBean(requiredType);
    	}
    
    	/**
    	 * 清除SpringContextHolder中的ApplicationContext为Null.
    	 */
    	public static void clearHolder() {
    		if (logger.isDebugEnabled()){
    			logger.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
    		}
    		applicationContext = null;
    	}
    
    	/**
    	 * 实现ApplicationContextAware接口, 注入Context到静态变量中.
    	 */
    	@Override
    	public void setApplicationContext(ApplicationContext applicationContext) {
    		SpringContextHolder.applicationContext = applicationContext;
    	}
    
    	public static  String getStatic(){
    		return SpringContextHolder.getApplicationContext().getApplicationName()+ "/static";
    	}
    	/**
    	 * 实现DisposableBean接口, 在Context关闭时清理静态变量.
    	 */
    	@Override
    	public void destroy() throws Exception {
    		SpringContextHolder.clearHolder();
    	}
    
    	/**
    	 * 检查ApplicationContext不为空.
    	 */
    	private static void assertContextInjected() {
    		Validate.validState(applicationContext != null, "applicaitonContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder.");
    	}
    }

     

    3.定义ResourceUtils类,用来加载国际化资源到缓存,或者更新缓存,要在对应的mapper执行插入,更新,删除后执行该utils对应的方法更新缓存,或者reload

    public class MessageResourceUtils {
    	private static Logger logger = LoggerFactory.getLogger(MessageResourceUtils.class);
    //可以使用别的方式,查询数据库
    	private static SysMessageResourceMapper sysMessageResourceMapper = SpringContextHolder.getBean(SysMessageResourceMapper.class);
            public static final String MESSAGE_SOURCE_KEY = "message.source.key";
    	static {
    		reloadCache();
    	}
    	
    	/**
    	 * 根据local获取bundle
    	 * @param locale
    	 * @return
    	 */
    	public static Properties getResourceProperties(Locale locale){
    		return getResourceMap().get(locale);
    	}
    	
    	/**
    	 * get resource map of all locale
    	 * @return
    	 */
    	public static Map<Locale, Properties> getResourceMap() {
    		Map<Locale, Properties> localeMap = (Map<Locale, Properties>) CacheUtils.get(MESSAGE_SOURCE_KEY);
    		if (localeMap == null) {
    			localeMap = generateLocaleProperties();
    			CacheUtils.put(MESSAGE_SOURCE_KEY, localeMap);
    		}
    		return localeMap;
    	}
    	
    	/**
    	 * reload message resource
    	 */
    	public static void reloadCache() {
    // 可以使用别的缓存方式,可以选择多种方式,在此不提供缓存实现
    		CacheUtils.put(MESSAGE_SOURCE_KEY, generateLocaleProperties());
    	}
    	/**
    	 * 清除Message缓存
    	 * @param user
    	 */
    	public static void clearCache(){
    		CacheUtils.remove(MESSAGE_SOURCE_KEY);
    	}
    	
    	/**
    	 * reload message 
    	 * @param locale locale
    	 */
    	public static void reloadLocaleMessage(Locale locale) {
    		if (locale == null) {
    			return;
    		}
    		Map<Locale, Properties> localeMap = getResourceMap();
    		SysMessageResource search = new SysMessageResource();
    		search.setLang(locale.toString());
    		List<SysMessageResource> resourcesList = sysMessageResourceMapper.findList(search);
    		Properties prop = new Properties();
    		for (SysMessageResource messageResource : resourcesList) {
    			if (StringUtils.isBlank(messageResource.getLang()) || StringUtils.isBlank(messageResource.getCode())) {
    				continue;
    			}
    			prop.put(messageResource.getCode(), StringUtils.defaultString(messageResource.getLabel()));
    		}
    		
    		localeMap.put(locale, prop);
    		
    	}
    	
    	/**
    	 * insert message
    	 * @param resource
    	 */
    	public static void insertMessageResource(SysMessageResource resource) {
    		if (StringUtils.isBlank(resource.getLang()) || StringUtils.isBlank(resource.getCode())) {
    			return;
    		}
    		Locale locale = org.springframework.util.StringUtils.parseLocaleString(resource.getLang());
    		
    		if (locale != null) {
    			Properties prop = getResourceProperties(locale);
    			if (prop == null) {
    				prop = new Properties();
    				getResourceMap().put(locale, prop);
    			}
    			prop.put(resource.getCode(), resource.getLabel());
    		}
    	}
    	
    	/**
    	 * update message
    	 * @param resource
    	 */
    	public static void updateMessageResource(SysMessageResource resource) {
    		insertMessageResource(resource);
    	}
    	
    	/**
    	 * delete message
    	 * @param resource
    	 */
    	public static void deleteMessageResource(SysMessageResource resource) {
    		if (StringUtils.isBlank(resource.getLang()) || StringUtils.isBlank(resource.getCode())) {
    			return;
    		}
    		Locale locale = org.springframework.util.StringUtils.parseLocaleString(resource.getLang());
    		
    		if (locale != null) {
    			Properties prop = getResourceProperties(locale);
    			if (prop != null) {
    				prop.remove(resource.getCode());
    			}
    		}
    	}
    	/**
    	 * load all message
    	 * @return
    	 */
    	private static Map<Locale, Properties> generateLocaleProperties() {
    		Map<Locale, Properties> localPropertiesMap = new HashMap<>();
    		List<SysMessageResource> resourcesList = Collections.emptyList();
    		
    		try {
    			resourcesList = sysMessageResourceMapper.findList(new SysMessageResource());
    		} catch (Exception e) {
    			// TODO Auto-generated catch block
    			logger.warn("Query messageResource from database error");
    		}
    		
    		for (SysMessageResource messageResource : resourcesList) {
    			if (StringUtils.isBlank(messageResource.getLang()) || StringUtils.isBlank(messageResource.getCode())) {
    				continue;
    			}
    			Locale locale = org.springframework.util.StringUtils.parseLocaleString(messageResource.getLang());
    			
    			if (locale != null) {
    				Properties prop = localPropertiesMap.get(locale);
    				if (prop == null) {
    					prop = new Properties();
    					localPropertiesMap.put(locale, prop);
    				}
    				prop.put(messageResource.getCode(), StringUtils.defaultString(messageResource.getLabel()));
    			}
    		}
    		
    		return localPropertiesMap;
    	}
    }

    4.定义message解析类,继承自spring的AbstractMessageSource :

    public class ResourceBundleMessageSource extends AbstractMessageSource {
    
    
    	public ResourceBundleMessageSource() {
    		super();
    
    	}
    
    	@Override
    	protected MessageFormat resolveCode(String code, Locale locale) {
    		
    		Properties prop = MessageResourceUtils.getResourceProperties(locale);
    		
    		if (prop != null) {
    			String label = prop.getProperty(code);
    			if (label != null) {
    				return createMessageFormat(label, locale);
    			}
    		}
    		return null;
    	}
    }

    5. spring-mvc.xml 配置对应的messageSource,以及拦截器

    <!-- 拦截器配置,拦截顺序:先执行后定义的,排在第一位的最后执行。-->
    	<mvc:interceptors>
    		<!-- 国际化操作拦截器 如果采用基于(请求/Session/Cookie)则必需配置 --> 
    	    <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />  
    	</mvc:interceptors>
    	
    	<!-- 支持Shiro对Controller的方法级AOP安全控制 begin-->
    	<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
    		<property name="proxyTargetClass" value="true" />
    	</bean>
    	
    	<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    		<property name="exceptionMappings">
    			<props>
    				<prop key="org.apache.shiro.authz.UnauthorizedException">error/403</prop>
    				<prop key="java.lang.Throwable">error/500</prop>
    			</props>
    			</property>
    	</bean>
    	<!-- 支持Shiro对Controller的方法级AOP安全控制 end -->
    
    	
    	
    		<!-- 国际化 -->
    	<bean id="baseMessageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
    	    <!-- 国际化信息所在的文件名 -->                     
    <!-- 	    <property name="basename" value="messages" /> -->
    	</bean>
    	
    	<bean id="messageSource" class="com.wenbo.common.bundle.ResourceBundleMessageSource">
    	    <!-- 如果在国际化资源文件中找不到对应代码的信息,就用这个代码作为名称  -->  
    	    <property name="parentMessageSource" ref="baseMessageSource" />             
    		<property name="useCodeAsDefaultMessage" value="true" />           
    	</bean>
    	
    <!-- session 方式 -->
    	<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver" >
    		<property name="defaultLocale" value="en_US" />
    	</bean>
    
         <!-- cookie 方式 
        <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver" />
    -->

    使用方式和使用properties国际化一样,可以参考:

    https://blog.csdn.net/D939030515/article/details/64906101

    展开全文
  • Thymeleaf - 使用方法及国际化(超详细)

    千次阅读 多人点赞 2020-03-17 10:43:49
    ②*{},选择变量表达式,可以省略对象名,直接获取属性值 ${user}"> *{name}">九月p> *P{age}">18p> div> ③#{},Message表达式,它主要是从国际化配置文件中取值,这里暂不做示例,文章后面将会示例从国际化配置...

    Thymeleaf简介

    Thymeleaf是一个和Velocity、FreeMarker 类似的模板引擎,它在有网络和无网络的环境下皆可运行。因为它支持html原型,在html的标签里增加了额外的属性来达到模板+数据的展示方式。浏览器解释html时会忽略未定义的标签属性,所以thymeleaf的模板可以静态地运行。当有数据返回到页面时,Thymeleaf标签会动态地替换掉静态内容,使页面动态显示。

    它与SpringBoot完美结合,SpringBoot提供了Thymeleaf的默认配置,并且为Thymeleaf设置了视图解析器。它可以快速实现表单绑定、属性编辑器、国际化等功能。

    在SpringBoot中的配置

    1、添加依赖
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    
    2、创建模板文件夹

    SpringBoot自动为Thymealf注册了一个视图解析器ThymealfViewResolver,并且配置了模板(HTML)的位置,与JSP类似的前缀+视图名+后缀的风格。我们可以进到它的配置文件(ThymeleafProperties)里去看:
    ​​​​​​Thymeleaf配置文件
    可以发现,如果我们没有在yml中进行配置,则它去找的视图文件默认是在resources配置文件夹下的templates文件夹里,后缀为.html。我们也可以在配置文件中进行配置,即图中所标注的spring.thymeleaf下。
    静态页面放置的位置
    yml配置文件
    yml配置文件

    Controller层返回页面示例

    在这里插入图片描述

    Thymeleaf在Html页面中的基本使用

    1.要在html页面中使用thymeleaf的标签,必须引入名称空间。
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    
    2.表达式的使用

    在html页面中,要使用thymealf的标签,只需在原标签名前加th:即可,如th:srcth:hrefth:text
    ${},变量表达式,它可以获取到Controller层存入Model的值

    // 后端Controller层代码
    model.addAttribute("name","柳成荫");
    User user = new User("柳成荫",22);   // 姓名、年龄
    model.addAttribute("user",user);
    
    // html页面中获取并显示
    <span th:text="${name}">九月</span>
    <span th:text="${user.age}">18</span>
    

    前面说过,当有数据传递过来时,则动态数据会替换静态数据。
    *{},选择变量表达式,可以省略对象名,直接获取属性值

    <div th:object="${user}">
    	<p th:text="*{name}">九月</p>
    	<p th:text="*P{age}">18</p>
    </div>
    

    #{},Message表达式,它主要是从国际化配置文件中取值,这里暂不做示例,文章后面将会示例从国际化配置文件中取值。

    3、URL的使用

    ①绝对网址,绝对URL用于创建到其他服务器的链接,需要指定协议名称http或者https,如:

    <a th:href="@{https://www.baidu.com}">百度</a>
    

    ②上下文相关URL,即与项目根相关联的URL,这里假设我们的war包为app.war,且没有在tomcat的server.xml配置项目的路径(Context),则在Tomcat启动之后,就会在webapps文件下产生一个app文件夹,此时app就是上下文路径(Context)

    <!-- 在页面这样写 -->
    <a th:href="@{/blog/search}">跳转</a>
    <!-- Thymeleaf解析之后是这样的 -->
    <a href="/app/blog/search">跳转</a>
    

    ③服务器相关URL,它与上下文路径很相似,它主要用于一个Tomcat下运行有多个项目的情况。比如说我们当前项目为app,如果tomcat还运行着一个otherApp,我们就可以通过该方法访问otherApp的请求路径。

    <!-- 在页面这样写 -->
    <a th:href="@{~/otherApp/blog/search}">跳转</a>
    <!-- Thymeleaf解析之后是这样的 -->
    <a href="/otherApp/blog/search">跳转</a>
    

    ④有时候我们需要页面带参数传递到后端,则可以使用下面这个方法

    <!-- 在页面这样写 -->
    <a th:href="@{/blog/search(id=3,blogName='Java')}" >跳转</a>
    <!-- Thymeleaf解析之后是这样的,这里忽略项目路径 -->
    <a href="/blog/search?id=3&blogName=Java" >跳转</a>
    
    <!-- 还可以这样写,实现restful风格的效果 -->
    <a th:href="/blog/{id}/search(id=3&blogName=Java)">跳转</a>
    <!-- Thymeleaf解析之后是这样的,这里忽略项目路径 -->
    <a href="/blog/3/search?blogName=java">跳转</a>
    
    4、字面值

    有时候,我们需要在指令中填入基本类型如:字符串、数值、布尔等,不希望Thymeleaf去给我们解析,可以这样做:
    ①字符串字面值

    <!-- 在页面中这样写 -->
    <span th:text="‘Thymealf’ + 3">templates</span>
    <!-- Thymeleaf解析后 -->
    <span>Thymealf3</span>
    

    ②数字字面值

    <!-- 在页面中这样写 -->
    <span th:text="1 + 3">templates</span>
    <!-- Thymeleaf解析后 -->
    <span>4</span>
    

    ③布尔字面值:为true和false

    5、拼接

    传统拼接需要用''来进行普通字符串和表达式的拼接,Thymeleaf中进行了简化,只需将拼接的内容块使用||包裹即可:

    <span th:text="|欢迎您,${user.name}|">九月</span>
    
    6、运算符

    ①因为html里会将<>符号进行解析,所以不能直接使用,但是如果在{}内使用,是不需要转换的

    >	      gt    即greater than,大于
    <         lt    即less than,小于
    >=        ge    即greater equal,大于等于
    <=        le    即less equal,小于等于
    

    ②三元运算符

    <span th:text="${false} ? '' : ''">性别</span>
    <>
    

    ③空值判断

    <!-- 如果user.name为空,则显示 空值显示 这几个字 -->
    <span th:text="${user.name} ?: ‘空值显示’"></span>
    
    7、内联写法

    [()],解析输出,会解析内容里的html标签

    <span>[(${user.name})]</span>
    

    [[]],原样输出,不会解析内容里的html标签

    <span>[[${user.name}]]</span>
    
    8、局部变量

    可以将后端传来的数据赋值给一个局部变量,这个局部变量只能在标签内部使用,外部是不可以的

    <div th:with="user=${userList[0]}">
    	<span th:text="${user.name}">昵称</span>
    </div>
    
    9、判断

    th:if,满足条件才显示标签包裹的(含标签)的内容

    <span th:if="${true}">显示</span>
    <span th:if="${user.age == 18}">18岁显示</span>
    

    th:unless,与th:if相反,不满足条件时显示

    <span th:unless='${true}'>不显示</span>
    

    th:switch,switch的效果一致

    <div th:witch="${user.name}">
    	<span th:case="柳成荫">柳成荫</span>
    	<span th:case="九月">九月</span>
    	<span th:case="寻宝">寻宝</span>
    </div>
    
    10、迭代

    th:each,迭代一个集合/数组,可以使用它的内置对象stat获取更多的信息。
    先列出内置对象stat的用法:

    index:角标,从0开始
    count:元素的个数,从1开始,当前遍历到第几个
    size:元素的总个数
    current:当前遍历到的元素
    even/odd:是否为奇/偶,都是返回true或false的布尔结果
    first/last:是否第一/最后,都是返回true或false的布尔结果
    

    使用th:each

    <tr th:each="user:${userList}">
    	<td th:text="${user.name}"></td>
    	<td th:text="|当前迭代到第${stat.count}个了|"></td>
    </tr>
    
    11、环境相关对象

    1、${#ctx}:上下文对象,可用于获取其他内置对象
    2、${#vars}:上下文变量。
    3、${#locale}:上下文区域设置。
    4、${#request}:HttpServletRequest对象。
    5、${#response}:HttpServletResponse对象。
    6、${#session}:HttpSession对象。
    7、${#servletContext}:ServletContext对象。
    具体怎么使用,可以自行百度,这里以session为例:

    <span th:text="${session.user.name}">存储在session的User对象</span>
    
    12、全局对象功能

    1、#strings:字符串工具类
    2、#lists:List 工具类
    3、#arrays:数组工具类
    4、#sets:Set 工具类
    5、#maps:常用Map方法。
    6、#objects:一般对象类,通常用来判断非空
    7、#bools:常用的布尔方法。
    8、#execInfo:获取页面模板的处理信息。
    9、#messages:在变量表达式中获取外部消息的方法,与使用#{…}语法获取的方法相同。
    10、#uris:转义部分URL / URI的方法。
    11、#conversions:用于执行已配置的转换服务的方法。
    12、#dates:时间操作和时间格式化等。
    13、#calendars:用于更复杂时间的格式化。
    14、#numbers:格式化数字对象的方法。
    15、#aggregates:在数组或集合上创建聚合的方法。
    16、#ids:处理可能重复的id属性的方法。
    具体怎么使用,可以自行百度,这里仅以#dates为例,格式化时间:

    <span th:text="${#dates.format(blog.updateTime)},'yyyy-MM-dd HH:mm:ss'"></span>
    
    13、class属性增加

    使用th:classappend,可以增加class元素类名。通常用在如导航栏上,当导航栏上某个标签被选中,它会与其他没有被选中的有不同的样式。

    <a class="item m-mobile-hide’ th:classappend='${n==1} ? ‘active’">首页</a>
    <!-- 如果n==1成立,则解析如下 -->
    <a class="item m-mobile-hide active’">首页</a>
    

    Thymeleaf在Html页面中的布局使用

    定义片段引入片段是非常好的功能,在一个项目中的各个页面里,通常他们大部分导航栏和底部是相同的。我们可以创建一个页面专门用来定义重复使用片段,然后在其他页面做引入。

    1、定义片段

    ①使用th:fragment定义通用片段 - 普通片段

    <nav th:fragment="header">
    	<a href="#">首页</a>
    	<a href="#">分类</a>
    </nav>
    

    ②使用th:fragment定义通用片段 - 带参片段
    如下代码,如果我们当前是在首页,则首页这个标签就会多一个样式,而分类就没有。我们只需要在引入这块片段的代码里传递一个参数过来进行判断即可。

    <nav th:fragment="header(n)">
    	<a class="item’ th:classappend='${n==1} ? ‘active’">首页</a>
    	<a class="item’ th:classappend='${n==2} ? ‘active’">分类</a>
    </nav>
    

    ③使用id来定义片段 - 不推荐

    <nav id="header">
    	<a href="#">首页</a>
    	<a href="#">分类</a>
    </nav>
    
    2、引入片段 - 下文中的_fragment为定义片段的html页面

    ①将公共的标签插入到指定标签中 - 该方法不可以省略~{}

    <div th:insert="~{_fragment::header}">
    	<!-- 被引入的片段会插入到div标签内部 -->
    </div>
    

    ②将公共的标签替换指定的标签 - 该方法可以省略~{}
    推荐使用该引入方式,这样做,可以保证在没有网络(动态数据)的情况下,其他页面也有内容可以显示。

    <!-- 被引入的片段会替换div标签的内容(含div) -->
    <div th:replace="~{_fragment::header}">
    	<a href="#">链接</a>
    </div>
    

    ③将公共的标签包含到指定的标签 - 该方法可以省略~{}

    <div th:include="~{_fragment::header}">
    	<!-- 
    		被引入的片段会被包含到div标签内部
    		但是最外层的标签不会被包含进来,即只包含th:fragment所在标签内部的内容
    	-->
    </div>
    

    ④上诉三种方法,可以用来引入使用id定义片段的 - 不推荐

    <nav th:insert="~{_fragment::#header}">
    	<!-- 不推荐id定义片段和引入这种片段 -->
    </nav>
    

    ⑤引入带参片段 - 重要

    <nav th:replace="header(2)">
    	<a class="item’ th:classappend='${n==1} ? ‘active’">首页</a>
    	<a class="item’ th:classappend='${n==2} ? ‘active’">分类</a>
    </nav>
    
    3、th:fragment更新数据

    很多时候,在页面里我们需要使用Ajax异步请求数据,我们希望只更新某一块的数据,其他地方不变。这时,就需要th:fragment来做了,文章前面有提到return "index :: blogList"这种方式,它就是配合th:fragment来做的。

    <!-- 其他代码 -->
    <div th:fragment="userInfo">
    	<span th:text="${user.name}">九月</span>
    	<span th:text="${user.age}">18</span>
    </div>
    <!-- 其他代码 -->
    
    public String userInfo(Model model){
    	model.addAttribute(user,new User("柳成荫",22));
    	return "index :: userInfo";
    }
    

    th:fragmentth:replace是用在合适的地方非常好用,常见的css块、js块、导航栏、底部区域。

    4、块级标签th:block

    th:block是thymeleaf提供的块级标签,其特殊性在于Thymeleaf模板引擎在处理<th:block>的时候会删掉它本身,标签本身不显示,而保留其内容
    ①常见用法 - HTML部分

    <!-- 控制几个标签是否一起显示/不显示 -->
    <th:block th:if="...">
    	<div id="div1">我和div2一起</div>
    	<div id="div2">我和div1一起</div>
    </th:block>
    
    <!-- 循环统计标签 -->
    <table>
    	<th:block th:each="...">
    		<tr>...</tr>
    		<tr>...</tr>
    	</th:block>
    </table>
    

    ②常见用法 - JS部分
    有时候js的引入可能也是其他页面公有的,我们想定义一个js片段,然后在其他页面引入。可以使用<div>js片段包裹起来,然后将这个<div>定义成一个片段,但这种做法并不好。这时就用到了th:block

    <!-- JS公用部分 - 定义片段 -->
    <th:block th:fragment="script">
        <script src="../static/js/jquery-3.3.1.min.js" th:src="@{/js/jquery-3.3.1.min.js}"></script>
        <script src="../static/js/semantic.min.js" th:src="@{/js/semantic.min.js}"></script>
    </th:block>
    
    <th:block th:replace="_fragment::script">
    	<!-- 这样就解决了JS共用的问题了 -->
    	<script src="../static/js/jquery-3.3.1.min.js"></script>
    </th:block>
    

    可能有人会问,这个th:block会不会影响静态页面?实际上,它是不会影响静态页面的。如果你实在是担心,我们可以用可以使用下面这种方法

    <!--/*/<th:block th:replace="_fragments :: script">/*/-->
    	<!-- 这样就解决了JS共用的问题了 -->
    	<script src="../static/js/jquery-3.3.1.min.js"></script>
    <!--/*/<th:block th:replace="_fragments :: script">/*/-->
    

    注释的使用

    可以看到,上面我们使用了<!-- -->的方式将th:block注释掉了,但是你也发现,我们在里面使用了/*/ /*/来包裹了标签。它是thymeleaf注释的一种,它在运行时,就不会把上面那些注释了的代码看做是注释,而是当做正常的处理。
    /*是另外一种,它的作用就是thymeleaf运行时,把包裹起来的内容注释掉。
    /*/注释

    <!-- 下面这行代码再静态页面中确实是被注释了,但是动态运行时,则不会被注释 -->
    <!--/*/<th:block th:replace="_fragments :: script">/*/-->
    

    /*注释

    <!--
        下面的注释在静态页面的确注释了,但是请注意,它注释的是/*和*/ 
    	而动态运行时,被/*和*/包裹的都要被注释掉
    	这样做的目的是保证静态运行时可以看到静态效果,动态运行时看到动态效果
    -->
    <div>
    	<span th:each="blog:${blogList}" th:text="${blog.name}">博客1</span>
    	<!-- /* -->
    	<span th:each="blog:${blogList}">博客2</span>
    	<span th:each="blog:${blogList}">博客3</span>
    	<span th:each="blog:${blogList}">博客4</span>
    	<!-- */ -->
    </div>
    

    JS模板预处理

    模板引擎不仅可以渲染html,也可以对JS中的进行预处理。而且为了在纯静态环境下可以运行。在script标签中通过th:inline="javascript"来声明这是要特殊处理的js脚本。

    <script th:inline="javascript">
    	var username = /*[[${user.name}]]*/ ‘默认’;
    	var age = /*[[${user.age}]]*/18;
    	console.log(username);
    	console.log(age);
    </script>
    

    国际化

    在SpringBoot项目里面做国际化,只需要在resources/i18n路径下创建xxx.propertiesxxx_en_US.propertiesxxx_zh_CN.properties即可。具体怎么做,将在下文进行演示。
    在SpringBoot中有一个messageSourceAutoConfiguration,它会自动管理国际化资源文件。
    messageSourceAutoConfiguration
    也就是说,我们在resources/i18n创建的国际化配置文件前缀名为message时,SpringBoot会自动加载。
    MessageSourceProperties
    当然,我们也可以自己定义国际化文件名称,这里我们将国际化配置文件命名为login
    1、去yml配置文件中配置国际化

    spring
      messages: i18n.login
    

    2、要确保文件编码是UTF-8,可以到idea的设置里去设置并让其自动转换,Editor->File Encodings
    文件编码
    3、创建i18n文件夹及3个国际化配置文件,如图
    其中login.properties是基础配置文件(默认),如果你的浏览器它是其他语言比如法语,是没有办法国际化的,所以它就会采用login.properties
    国际化配置文件
    在idea里只要创建两个国际化的配置文件就会自动加入到Resource Bundle 'xxx',当然我们还是需要创建三个properties配置文件。后缀必须按格式写,_en_US就是英文,_zh_CN就是中文。
    4、编写国际化
    我们直接点进login.properties,可以看到下方有个切换到Resource Bundle的按钮,点击切换,添加国际化,然后分别在右侧写上对应国际化语言
    国际化
    想添加多少,就可以添加多少。
    5、可以在Thymeleaf页面进行获取了,用到前面所说的#{}

    <button type="submit" th:text="#{login.btn}"></button>
    

    6、一般来说我们在页面会做个切换中英文的按钮来进行切换

    <a th:href="@{/login(lan='zn_CN')}">中文</a>
    <a th:href="@{/login(lan='en_US')}">英文</a>
    

    7、做完上诉步骤,我们还需要编写一个本地解析器来进行解析

    public class MyLocaleResolver implements LocaleResolver {
        @Override
        public Locale resolveLocale(HttpServletRequest request) {
            // 接收语言的参数 - 传进来的就是形如'zh_CN'这样的参数
            String lan = request.getParameter("lan");
            // 使用默认的语言 - 在文中就是login.properties文件里配置的
            Locale locale = Locale.getDefault();
            // 判断接收的参数是否为空,不为空就设置为该语言
            if(!StringUtils.isEmpty(lan)){
                // 将参数分隔 - 假设传进来的是'zh_CN'
                String[] s = lan.split("_");
                // 语言编码:zh   地区编码:CN
                locale = new Locale(s[0],s[1]);
            }
            return locale;
        }
    
        @Override
        public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
    
        }
    }
    

    8、我们还需要写一个配置文件来表明国际化解析器是用的我们自己写的

    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
    	@Bean
        public LocaleResolver localeResolver(){
            return new MyLocaleResolver();
        }
    }
    

    国际化就这样完成了,通过上面定义的切换语言按钮就可以切换了。

    展开全文
  • Android 实现国际化

    千次阅读 2020-03-31 14:14:58
    这里我们不得不说一个重要的方法attachBaseContext(),请自行查阅,我就不介绍了 ,重点介绍实现国际化 思路: 一般我们做项目都会用到BaseActivity ,这里面实现如下方法 @Override protected void ...
  • 项目国际化(i18n)

    千次阅读 2018-10-08 19:05:56
    国际化开发概述 软件的国际化:软件开发时,要使它能同时应对世界不同地区和国家的访问,并针对不同地区和国家的访问,提供相应的、符合来访者阅读习惯的页面或数据。 国际化(internationalization)又称为i18n...
  • Nacos实现SpringBoot国际化的增强

    千次阅读 2020-03-06 10:59:17
    我们知道,SpringBoot默认提供了国际化支持,但是它存在配置变更不灵活,统一管理缺乏等问题。因此,本文将基于上述问题,介绍通过Nacos实现SpringBoot国际化的增强。
  • 什么是国际化
  • 《数字货币与人民币国际化》读书笔记1 (本文为课后作业,阅读《数字货币与人民币国际化》(主编:郑润祥),并根据书的内容进行学习、整理和总结,在此记录;第一部分针对本书人民币国际化的内容) 第一章 货币...
  • vue项目实现国际化方案

    万次阅读 2020-12-03 16:51:09
    报道指挥实现国际化方案 目标:实现报道指挥多语言切换功能(直接通过网页链接获取参数切换中英文)。 采用方式:使用国际化插件vue-i18n,同时需要手动编写语言(即:将项目中所有文字全部提出,增加其文字版本)。...
  • iOS开发之国际化(本地化)

    千次阅读 2018-02-28 14:40:48
    1.在App开发之初,就已经有了国际化开发的Future,这种情况下进行国际化是很容易的,只要在开发过程中把需要国际化的字符串进行简单的处理即可。 2.已经开发完毕,开发之初并没有进行国际化适配,突然来需求说需要...
  • Spring Boot Validator以及国际化(i18n)

    万次阅读 2019-10-02 01:20:57
    本文实验的软件环境如下: Spring Boot 2.1.8.RELEASE JDK 1.8 IDEA2019.2.3 Gradle 5.6.2 最近在研究Spring Boot 的Validator以及国际化
  • 软件研发中经常听到这几个名词,本地化、国际化、全球化。分别又代表什么意思呢? 一、本地化 本地化,英文对应Localization,缩写为L10N,其中L为首字母,N是尾字母,10表示在首字母的L和尾字母的N之间省略了10个字母...
  • Flutter(十七) 实现国际化

    千次阅读 2020-06-01 14:13:51
    一. 国际化的认识 二. 国际化的适配 三. 国际化的工具
  • springboot实现国际化居然可以这么简单

    千次阅读 多人点赞 2020-03-15 13:54:09
    springboot实现国际化(多语言) 国际化(internationalization)是设计和制造容易适应不同区域要求的产品的一种方式。它要求从产品中抽离所有地域语言,国家/地区和文化相关的元素。换言之,应用程序的功能和代码...
  • Spring Validation及消息国际化.md

    千次阅读 热门讨论 2018-09-19 09:51:23
    因此,如果要考虑国际化,那么在message中就需要指定消息的code而不是消息本身,然后在国际化文件中配置该code对应的消息内容。Hibernate默认会加载Classpath下名称为ValidationMessages.properties的文件,因此,...
  • 前端实现国际化

    千次阅读 2017-12-08 10:29:22
    1、strings_zh.properties (表示中文属性文件)新创2、strings.properties (表示默认情况下属性文件)新创3、jquery.i18n.properties-min-1.0.9.js(国际化需要文件)4、jquery.js  2. 3.属性文件配置 . 4.html ...
  • vue动态加载VueI18n实现国际化

    千次阅读 2019-08-14 21:49:23
    前一段时间给大家写了一个基于spring boot和iview的前后端分离架构,当时国际化的语言信息是直接在前端直接事先配置好的,因此当我们感觉那个国际化不合适的时候我们都需要在前端去维护我们的国际化信息,因此就想有...
  • Android自适应国际化语言

    万次阅读 热门讨论 2016-09-21 12:14:27
    转载:Android学习—-自适应国际化语言Android Studio一键生成快速开发实现语言国际化有关Android国际化的一点积累国际化为支持多国语言,在res/中创建一个额外的values目录以连字符和ISO国家代码结尾命名,比如...
  • 什么是国际化呢?国际惯例,来时来一段官方介绍: 国际化(internationalization)是设计和制造容易适应不同区域要求的产品的一种方式。它要求从产品中抽离所有地域语言,国家/地区和文化相关的元素。换言之,应用...
  • Android App 国际化详解

    千次阅读 2019-03-01 17:11:50
    前言 internationalization (国际化)简称 i18n,因为在i和n之间还有18个字符,localization(本地化)...Android没有专门的API来提供国际化,而是通过对不同resource的命名来达到国际化的目的,同时 这种命名方法还可...
  • 《数字货币与人民币国际化》读书笔记2 (本文为课后作业,阅读《数字货币与人民币国际化》(主编:郑润祥 电子工业出版社),并根据书的内容进行学习、整理和总结,在此记录;第二部分针对本书数字货币的内容) 第...
  • springboot实现国际化

    万次阅读 2019-07-08 18:32:38
    在springmvc中实现国际化的步骤一般分为以下几步: 1)、编写国际化配置文件; 2)、使用ResourceBundleMessageSource管理国际化资源文件 3)、在页面使用fmt:message取出国际化内容 在springboot中自动装配好了国际...
  • Java国际化及Spring国际化解决方法

    千次阅读 2015-11-03 15:08:55
    假设我们正在开发一个支持多国语言的Web应用程序,要求系统能够根据客户端的系统的语言类型返回对应的界面:英文的操作系统返回英文界面,而中文的操作系统则返回中文界面——这便是典型的i18n国际化问题。...
  • 最近参与了一个境外钱包的项目,要求是实现SpringMVC的动态国际化。之前用Spring做国际化,都将国际化信息写在properties文件中。这次在项目中遇到一个需求,需要把properties文件去掉,直接从数据库读取国际化信息...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 420,622
精华内容 168,248
关键字:

国际化