spring中i18n国际化处理多语言

3 篇文章 0 订阅

前言

在项目中,往往用户会存在多语言的述求,比如说一个系统既有中文的用户,又有英文的用户。怎么来实现多语言呢?

首先前后端分离的项目,前端会有自己的多语言实现方案,大致效果就是,用户切换语言,那些静态的按钮,菜单,标签等前端都可以自己切换。但是调用后端由后端返回的异常提示,消息体等,也需要后端实现多语言,大致的实现方案就是由前端传入一个参数,表示是期望后端提供什么类型的语言的消息体,后端就可以通过这个来实现国际化的消息了。本文介绍的是采用spring的国际化支持来实现国际化语言。

一、spring中国际化相关概念及理论知识

MessageSource接口

spring中国际化是通过MessageSource这个接口来支持的,该接口中定义了3个获取国际化消息的方法。

org.springframework.context.MessageSource

public interface MessageSource {
    /**
     * 获取国际化信息
     * @param code 表示国际化资源中的属性名;
     * @param args用于传递格式化串占位符所用的运行期参数;
     * @param defaultMessage 当在资源找不到对应属性名时,返回defaultMessage参数所指定的默认信息;
     * @param locale 表示本地化对象
     */
    @Nullable
    String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);
    /**
     * 与上面的方法类似,只不过在找不到资源中对应的属性名时,直接抛出NoSuchMessageException异常
     */
    String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;
    /**
     * @param MessageSourceResolvable 将属性名、参数数组以及默认信息封装起来,它的功能和第一个方法相同
     */
    String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}

国际化消息的三个实现类

ResourceBundleMessageSource

这个是基于Java的ResourceBundle基础类实现,允许仅通过资源名加载国际化资源

spring boot中默认使用该类实现。

读取配置文件中配置的国际化信息

在application.yml中配置一下配置就能实例

spring:
  messages:
    #resources/i18n/messages_zh_CN.properties  ,取i18n/messages,规则为取文件目录开始,文件前缀结束
    basename: i18n/messages
    # 国际化编码
    encoding: utf-8
    # 在找不到当前系统对应的资源文件时,如果该属性为 true,则会默认查找当前系统对应的资源文件,否则就返回 null,返回 null 之后,最终又会调用到系统默认的 messages.properties 文件
    fallback-to-system-locale: true

ReloadableResourceBundleMessageSource

这个功能和第一个类的功能类似,多了定时刷新功能,允许在不重启系统的情况下,更新资源的信息

该类也是读取配置文件中的国际化信息

实例化该类的方案如下 

    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasenames("classpath:i18n/messages");
        messageSource.setDefaultEncoding("utf-8");
        //设置缓存时间1个小时 1*60*60*1000毫秒
        //可以设置成-1表示永久缓存,设置成0表示每次都从文件中读取
        messageSource.setCacheMillis(1*60*60*1000);
        messageSource.setFallbackToSystemLocale(true);

        return messageSource;
    }

StaticMessageSource

它允许通过编程的方式提供国际化信息。

比如说配置好信息存放在数据库中,自己可以自定义去db中获取

spring中使用国际化的步骤

通常我们使用spring的时候

1、国际化语言配置文件,

通常在src/main/resources目录下建i18n文件夹,再创建各类语言的properties资源文件

i18n/messages.properties
i18n/messages_en_US.properties
i18n/messages_zh_CN.properties

当然也可以将国际化语言资源包放置在其他载体中,如数据库。

2、向容器中注册一个MessageSource类型的bean,bean名称必须为:messageSource

实现类可以用上面说的三种中选择一种

3、从spring容器中获取MessageSource类型的bean,调用getMessage方法获取配置的语言内容

4、如果是web类型的程序,语言的选择应该还要根据请求来定。一般情况,可以让前端传一个语言参数,参数可以放置在cookie,header,parameter都可以。然后通过一个拦截器获取到参数,传递给线程变量,在获取语言的时候,从线程变量中取出该值,就可以动态的根据请求获取对应的语言信息了。

二、一个国际化的demo例子

需要自定义的几个类及文件

com.shenyun.flinkgui.i18n.constant.LanguageBaseConstant 类


package com.shenyun.flinkgui.i18n.constant;

public class LanguageBaseConstant {
    public static final String LOCALE_LANGUAGE_KEY = "language";
}

com.shenyun.flinkgui.i18n.interceptor.LocaleChangeInterceptor 类

package com.shenyun.flinkgui.i18n.interceptor;


import com.shenyun.flinkgui.i18n.constant.LanguageBaseConstant;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.util.WebUtils;

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

public class LocaleChangeInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // cookie :language=en_US
        // header :language=en_US
        // parameter :language=en_US
        //通过制定cookie,header,param
        // 获取本地语言的优先级  cookie<header<parameter
        String locale="";

        //1、处理cookie中带有的语言信息
        Cookie cookie = WebUtils.getCookie(request, LanguageBaseConstant.LOCALE_LANGUAGE_KEY);
        String cookieLocale="";
        if (cookie!=null) {
            // Proceed in cookie
            cookieLocale=cookie.getValue();
        }
        if(cookieLocale!=null&& !cookieLocale.trim().equals("")){
            locale=cookieLocale;
        }

        // 2、每次请求的header中可以单独指定语言  language=zh_CN
        String headerLocale = request.getHeader(LanguageBaseConstant.LOCALE_LANGUAGE_KEY);
        if(headerLocale!=null && !headerLocale.trim().equals("")){
            locale=headerLocale;
        }

        // 3、从get 参数数据获取 language=zh_CN
        String parameterLocale=request.getParameter(LanguageBaseConstant.LOCALE_LANGUAGE_KEY);
        if(parameterLocale!=null && !parameterLocale.trim().equals(""));{
            locale=parameterLocale;
        }

        //4 语言信息写入线程变量
        if (locale!=null) {
            LocaleContextHolder.setLocale(parseLocaleValue(locale));
        }

        return true;
    }

    @Nullable
    protected Locale parseLocaleValue(String localeValue) {
        return StringUtils.parseLocale(localeValue);
    }

}

因LocaleChangeInterceptor类是我自己自定义实现的,逻辑为,可以通过cookie,header,parameter传入使用的指定的语言类型。大致情况如下。

// cookie :language=en_US
// header :language=en_US
// parameter :language=en_US
// 通过制定cookie,header,param
// 获取本地语言的优先级  cookie<header<parameter

spring也有默认的实现,就看自己怎么选择了。

com.shenyun.flinkgui.i18n.utils.MessageResolverUtils 类

package com.shenyun.flinkgui.i18n.utils;

import cn.hutool.extra.spring.SpringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;

import java.util.Arrays;

/**
 * desc: 获取i18n资源文件
 */
public class MessageResolverUtils {

    @Autowired
    private static MessageSource messageSource = SpringUtil.getBean(MessageSource.class);

    public MessageResolverUtils() {
    }

    /**
     * 根据 messageKey 获取国际化消息 委托给 spring messageSource
     *
     * @param code 消息key
     * @return 解析后的国际化
     */
    public static String getMessage(Object code) {
        return messageSource.getMessage(code.toString(), null, code.toString(), LocaleContextHolder.getLocale());
    }

    /**
     * 根据 messageKey 和参数 获取消息 委托给 spring messageSource
     *
     * @param code        消息key
     * @param messageArgs 参数
     * @return 解析后的国际化
     */
    public static String getMessages(Object code, Object... messageArgs) {
        Object[] objs = Arrays.stream(messageArgs).map(MessageResolverUtils::getMessage).toArray();
        String message =
                messageSource.getMessage(code.toString(), objs, code.toString(), LocaleContextHolder.getLocale());
        return message;
    }
}

com.shenyun.flinkgui.configure.AppConfiguration 类

package com.shenyun.flinkgui.configure;


import com.shenyun.flinkgui.i18n.constant.LanguageBaseConstant;
import com.shenyun.flinkgui.i18n.interceptor.LocaleChangeInterceptor;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;

import java.util.Locale;


@Configuration
public class AppConfiguration implements WebMvcConfigurer {

    @Bean(name = "localeResolver")
    public LocaleResolver localeResolver() {
        CookieLocaleResolver localeResolver = new CookieLocaleResolver();
        localeResolver.setCookieName(LanguageBaseConstant.LOCALE_LANGUAGE_KEY);
        // 设置默认语言,简体中文
        localeResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
        // set language tag compliant
        localeResolver.setLanguageTagCompliant(false);
        return localeResolver;
    }

    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor() {
        return new LocaleChangeInterceptor();
    }

    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasenames("classpath:i18n/messages");
        messageSource.setDefaultEncoding("utf-8");
        //设置缓存时间1个小时 1*60*60*1000毫秒
        //可以设置成-1表示永久缓存,设置成0表示每次都从文件中读取
        messageSource.setCacheMillis(1*60*60*1000);
        messageSource.setFallbackToSystemLocale(true);

        return messageSource;
    }

    // 注册拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localeChangeInterceptor());
    }
}

 配置message资源文件

默认资源messages.properties

unknown.i18n=未知的国际化信息,请检查。。。

# response messages
response.get.success=获取成功

# validation messages
param.test.ok=参数 {0} 的值 {1}

英文配置messages_en_US.properties

unknown.i18n=Unknown i18n information, please check. . .

# response messages
response.get.success = Get success

# validation messages
param.test.ok=parmam {0} value is {1}

中文配置messages_zh_CN.properties,与默认配置保持一样即可

国际化语言使用

//根据配置的key获取国际化msg
String msg=MessageResolverUtils.getMessage("response.get.success");

以下是在controller层的一个使用例子,具体的情况根据自身的业务情况

@Controller
@RequestMapping("language")
public class LanguageController {


    @ResponseBody
    @RequestMapping("getLanDemo")
    public String getLanDemo(){
        return MessageResolverUtils.getMessage("response.get.success");
    }
    @ResponseBody
    @RequestMapping("getLanDemo2")
    public String getLanParamDemo(String name){
        return MessageResolverUtils.getMessages("param.test.ok","name",name);
    }
}

三、国际化消息配置在数据库中的思路

国际化消息实现类中,有一个StaticMessageSource,这个类中有2个方法比较重要

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

自定义一个StaticMessageSource类

public class MessageSourceFromDb extends StaticMessageSource implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        //此处我们在当前bean初始化之后,模拟从db中获取国际化信息,然后调用addMessage来配置国际化信息
        this.addMessage("desc", Locale.CHINA, "我是从db来的信息");
        this.addMessage("desc", Locale.UK, "MessageSource From Db");
    }
}

上面的类实现了spring的InitializingBean接口,重写afterPropertiesSet方法,这个方法会在当前bean初始化之后调用,在这个方法中模拟从db中获取国际化信息,然后调用addMessage来配置国际化信息

使用上面的类,实现MessageSource接口的实例化类即可从数据库中获取国际化消息,但是数据的自己设计表结构。

参考项:

1、文章:https://blog.csdn.net/ysj_2021/article/details/125650190

2、项目:Dinky: Dinky 是一个开箱即用的一站式实时计算平台,以 Apache Flink 为基础,连接 OLAP 和数据湖等众多框架,致力于流批一体和湖仓一体的建设与实践。 

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Springi18n国际化功能可以通过配置properties资源文件来实现。通常,我们会在src/main/resources目录下创建一个i18n文件夹,并在其创建各类语言的properties资源文件,例如i18n/messages.properties、i18n/messages_en_US.properties、i18n/messages_zh_CN.properties等。\[1\] 在配置文件,我们可以使用yaml格式进行配置。例如,可以使用spring.messages.basename属性来指定properties文件的路径,如spring.messages.basename: i18n.login。这样,Spring就会根据配置的路径去读取相应的国际化资源文件。\[2\] 另外,如果你在前端使用jQuery,你也可以使用jQuery.i18n.properties插件来实现国际化。该插件可以根据用户指定的语言和国家编码来解析对应的.properties资源文件,从而实现前端的国际化功能。\[3\] #### 引用[.reference_title] - *1* [springi18n国际化处理语言](https://blog.csdn.net/shenyunsese/article/details/128326378)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [SpringBoot -> 国际化(i18n)](https://blog.csdn.net/rod0320/article/details/110086280)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Spring国际化i18n](https://blog.csdn.net/daobuxinzi/article/details/127982064)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值