网关的国际化改造和web服务的改造有所不同。
问题
SpringCloud Gateway是基于reactor模型的,按照SpringBoot那套以及所尝试网上以及AI的i18n国际化方案,都没有成功。
解决问题
基本思路跟SpringBoot项目的i18n一样
通过MessageSource加载messages国际化资源
通过基于WebFilter的过滤器从请求头获取Content-Language识别locale
需要国际化的地方通过MessageUtils.message()实现国际化
项目结构
配置文件(application.yml)
配置资源文件路径
spring:
messages:
basename: messages,commons # 多个资源文件路径
encoding: UTF-8
配置i18n过滤器
/**
* 国际化过滤器
*
*
* @Description
* @Author
* @Date 2024/4/29
*/
@Component
@Order(-1)
public class I18nGlobalFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
Locale locale = Locale.getDefault(); // 默认Locale
HttpHeaders headers = exchange.getRequest().getHeaders();
String language = headers.getFirst("Accept-Language");
if (language != null) {
locale = Locale.forLanguageTag(language);
}
// 将Locale存储在ServerWebExchange的属性中,供后续逻辑使用
exchange.getAttributes().put(Locale.class.getName(), locale);
// spring gateway手动处理,基于LocaleContextHolder
LocaleContextHolder.setLocale(locale);
SystemEnum.SERVICE_TEMPORARILY_UNAVAILABLE.getMessage();
return chain.filter(exchange);
}
}
工具类
import com.siemens.tbds.gateway.util.SpringUtils;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
/**
* 获取i18n资源文件
*
* @Description
* @Author
* @Date 2024/4/29
*/
public class MessageUtils {
/**
* 根据消息键和参数 获取消息 委托给spring messageSource
*
* @param code 消息键
* @param args 参数
* @return 获取国际化翻译值
*/
public static String message(String code, Object... args) {
MessageSource messageSource = SpringUtils.getBean(MessageSource.class);
return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());
}
}
要点1:
与常规SpringBoot的web项目不同之处在Filter,SpringBoot项目实现LocaleResolver接口即可(当然理论上跟SpringCloud gateway一样集成WebFilter也是可以的,因为WebFilter是spring-web里的接口);
要点2
网上文章以及AI的回答均是通过实现SpringCloud gateway的GlobalFilter接口,我最终都没有成功,根据资料显示GloabalFilter是进入gateway的routes路由的接口才会触发;(而我这里是网关模块自己有接口给前端调用,这个接口的国际化无法通过实现GlobalFilter实现。)
然而按照上述方法实现后将网关上到生产后却出现了问题。
问题
虽然在我本机上测试没有问题,但在Linux服务器上却出现了很多问题
问题1:
Locale.getDefault()在Linux的默认语言为en,在我本机上却是中文(zh-CN还是zh忘了)。
解决办法:
使用Locale.CHINA
问题2:
Locale.forLanguageTag(language);这个方法用于根据语言标签字符串创建一个 Locale
对象,但
浏览器传的值类似于
zh-CN,zh;q=0.9,en;q=0.8
这个方法会尝试解析这个字符串,解析为zh,并不是权重最高的zh-CN
在我本机上虽然资源文件为zh-CN但依然 能够识别到,但在Linux环境下就不行了
解决方法:
@Component
public class I18nGlobalFilter implements WebFilter,Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
Locale locale = Locale.CHINA; // 默认Locale
HttpHeaders headers = exchange.getRequest().getHeaders();
// 浏览器传递的语言偏好值
String language = headers.getFirst("Accept-Language");
if (language != null) {
// 解析语言偏好值
List<Locale.LanguageRange> ranges = Locale.LanguageRange.parse(language);
// 获取优先级最高的语言标签
locale = Locale.forLanguageTag(ranges.get(0).getRange());
}
// 将Locale存储在ServerWebExchange的属性中,供后续逻辑使用
exchange.getAttributes().put(Locale.class.getName(), locale);
// spring gateway手动处理,基于LocaleContextHolder
LocaleContextHolder.setLocale(locale);
return chain.filter(exchange);
}
@Override
public int getOrder() {
return HIGHEST_PRECEDENCE+500;
}
}