后端使用WebSocket主动向前端推送数据,该数据做国际化时,只显示中文的一种解决方式
近期,项目中碰到一个问题:使用WebSocket向前端推送一个弹框信息,弹框信息会根据语言环境不同显示不同语言。但是在使用i18n做国际化的时候,只能显示中文。
1 修改之前
1.1 国际化配置
首先,国际化配置是做正确了的。
该文件里的值也是正确的key=value的形式。``aaa.bbb.ccc=成功,aaa.bbb.ccc=success`。
1.2 application.yml文件
application.yml文件也是正确配置了的。
spring:
messages:
### 配置resource bundle资源文件的前缀名eg:i18n是文件夹名, device-messages是资源文件名, 支持的符号有'.'号或者'/'
basename: system_parameter/i18n/device-message
1.3 使用MessageSource调用i18n国际化。
public final class MessageSourceUtils {
private static final MessageSource MESSAGE_SOURCE = SpringContextHolder.getBean(MessageSource.class);
/**
* default constructor.
*/
private MessageSourceUtils() {
}
/**
* getMessage.
*
* @param key key
* @return value
*/
public static String getMessage(String key) {
return MESSAGE_SOURCE.getMessage(key, null, LocaleContextHolder.getLocale());
}
package org.springframework.context;
import java.util.Locale;
import org.springframework.lang.Nullable;
public interface MessageSource {
@Nullable
String getMessage(String var1, @Nullable Object[] var2, @Nullable String var3, Locale var4);
String getMessage(String var1, @Nullable Object[] var2, Locale var3) throws NoSuchMessageException;
String getMessage(MessageSourceResolvable var1, Locale var2) throws NoSuchMessageException;
}
1.4 语言环境配置
@Configuration
public class NativeLocaleConfig implements WebMvcConfigurer {
private final WebMvcProperties webMvcProperties;
public NativeLocaleConfig(WebMvcProperties webMvcProperties) {
this.webMvcProperties = webMvcProperties;
}
@Bean
public LocaleResolver localeResolver() {
final NativeLocaleResolver nativeLocaleResolver = new NativeLocaleResolver();
nativeLocaleResolver.setDefaultLocale(webMvcProperties.getLocale());
return nativeLocaleResolver;
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
final LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName(LocaleChangeInterceptor.DEFAULT_PARAM_NAME);
return localeChangeInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor()).addPathPatterns("/**");
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
}
}
public class NativeLocaleResolver implements LocaleResolver {
private static final Logger LOGGER = LoggerFactory.getLogger(NativeLocaleResolver.class);
@Nullable
private Locale defaultLocale;
/**
* setter.
*
* @param defaultLocale Locale
*/
public void setDefaultLocale(@Nullable Locale defaultLocale) {
this.defaultLocale = defaultLocale;
}
/**
* getter.
*
* @return Locale
*/
@Nullable
public Locale getDefaultLocale() {
return defaultLocale;
}
@Override
public Locale resolveLocale(HttpServletRequest request) {
final Locale tempDefaultLocale = getDefaultLocale();
final String acceptLang = request.getHeader("Accept-Language");
if (tempDefaultLocale != null && acceptLang == null) {
// Accept-Language为空时返回配置文件配置的默认语言
LogUtils.warn(LOGGER, "Http request header can not contains 'Accept-Language'.");
return tempDefaultLocale;
} else if (StringUtils.contains(acceptLang, "_")) {
final String[] split = acceptLang.split("_");
return new Locale(split[0], split[1]);
}
return new Locale("zh", "CN");
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
// Do nothing
}
}
1.5 WebSocket配置
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
@ServerEndpoint("/ws/{uniqueId}")
@Component
public class WebSocketServer {
private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketServer.class);
public static final String KEY = "DCG_FORE_HEARTBEAT";
/**
* concurrent包的线程安全Set, 用来存放每个客户端对应的MyWebSocket对象.
*/
private static final ConcurrentHashMap<String, WebSocketServer> WEB_SOCKET_CLIENT_LIST = new ConcurrentHashMap<>();
/**
* ping定时器
*/
private static final Timer TIMER;
/**
* 与某个客户端的连接会话, 需要通过它来给客户端发送数据.
*/
private Session session;
/**
* 接收uniqueId.
*/
private String uniqueId = "";
static {
TIMER = new Timer("websocket-ping-timer");
TIMER.schedule(new TimerTask() {
@Override
public void run() {
pingOpenConnections();
}
}, 1000, 60000);
}
/**
* sendHeartCheck:
* 发送检测心跳 <br/>
*/
private static void pingOpenConnections() {
if (!WEB_SOCKET_CLIENT_LIST.isEmpty()) {
WEB_SOCKET_CLIENT_LIST.forEach((id, websocketServer) -> {
try {
if (KEY.equalsIgnoreCase(id)) {
DcgForeStatusUtils.cacheDcgForeStatus(true);
}
websocketServer.sendPing();
} catch (Exception e) {
LogUtils.error(LOGGER,
ErrorLevel.buildErrorLevel1(DCGErrorCode.DCG_COMMON_ERROR),
"websocket user: {}, sendHeartCheckMsg error:!", id, e);
}
});
}
}
/**
* sendHeartCheckMsg:发送心跳消息 <br/>
* Description <br/>
*
* @throws IOException
* @throws IllegalArgumentException
*/
private void sendPing() throws IllegalArgumentException, IOException {
LogUtils.infoWithOutTChain(LOGGER, "websocket send PING to: {}", this.uniqueId);
if (this.session.isOpen()) {
this.session.getBasicRemote().sendPing(ByteBuffer.wrap(new byte[10]));
}
}
@OnOpen
public void onOpen(Session session, @PathParam("uniqueId") String uniqueId) {
this.session = session;
this.uniqueId = uniqueId;
WEB_SOCKET_CLIENT_LIST.remove(uniqueId);
WEB_SOCKET_CLIENT_LIST.put(uniqueId, this);
LogUtils.infoWithOutTChain(LOGGER, "用户连接: {}, 当前连接数为: {}.", uniqueId, WEB_SOCKET_CLIENT_LIST.size());
try {
sendMessage("连接成功");
} catch (Exception e) {
ErrorLevel errorLevel = ErrorLevel.buildErrorLevel1(DCGErrorCode.DCG_COMMON_ERROR);
LogUtils.error(LOGGER, errorLevel, "用户: {},网络异常!!!!!!", uniqueId);
}
if (KEY.equalsIgnoreCase(uniqueId)) {
DcgForeStatusUtils.cacheDcgForeStatus(true);
}
}
@OnClose
public void onClose() {
LogUtils.infoWithOutTChain(LOGGER, "用户退出心跳: {}.", uniqueId);
WEB_SOCKET_CLIENT_LIST.remove(uniqueId);
if (KEY.equalsIgnoreCase(uniqueId)) {
DcgForeStatusUtils.cacheDcgForeStatus(false);
}
LogUtils.infoWithOutTChain(LOGGER, "用户退出: {}, 当前连接数为: {}.", uniqueId, WEB_SOCKET_CLIENT_LIST.size());
}
@OnMessage
public void onMessage(String message, Session session) {
LogUtils.infoWithOutTChain(LOGGER, "用户消息: {}, 报文: {}", this.uniqueId, message);
if (StringUtils.isNotBlank(message)) {
transportMessage(message);
}
}
/**
* 消息转发
*
* @param message 原消息
*/
private void transportMessage(String message) {
try {
final JSONObject jsonObject = JSON.parseObject(message);
jsonObject.put("fromUniqueId", this.uniqueId);
String toUniqueId = jsonObject.getString("toUniqueId");
if (StringUtils.isNotBlank(toUniqueId) && WEB_SOCKET_CLIENT_LIST.containsKey(toUniqueId)) {
WEB_SOCKET_CLIENT_LIST.get(toUniqueId).sendMessage(jsonObject.toJSONString());
} else {
ErrorLevel errorLevel = ErrorLevel.buildErrorLevel1(DCGErrorCode.DCG_COMMON_ERROR);
LogUtils.error(LOGGER, errorLevel, "请求的userId: {} 不在该服务器上.", toUniqueId);
}
} catch (Exception e) {
ErrorLevel errorLevel = ErrorLevel.buildErrorLevel1(DCGErrorCode.DCG_COMMON_ERROR);
LogUtils.error(LOGGER, errorLevel, "sendMessage failed.", e);
}
}
@OnError
public void onError(Session session, Throwable cause) {
ErrorLevel errorLevel = ErrorLevel.buildErrorLevel1(DCGErrorCode.DCG_COMMON_ERROR);
LogUtils.error(LOGGER, errorLevel, "用户错误: {}. 发送信息失败: {}", this.uniqueId, cause);
}
public void sendMessage(String message) throws IOException {
if (this.session != null && this.session.isOpen()) {
synchronized (this.session) {
this.session.getBasicRemote().sendText(message);
}
}
}
/**
* 关闭所有ws服务
*/
public void closeWebSocketServer() {
stopPingTrigger();
for (Map.Entry<String, WebSocketServer> server : WEB_SOCKET_CLIENT_LIST.entrySet()) {
WebSocketServer webSocketServer = server.getValue();
Session tempSession = webSocketServer.session;
if (null != tempSession) {
try {
tempSession.close();
} catch (Exception e) {
ErrorLevel errorLevel = ErrorLevel.buildErrorLevel1(DCGErrorCode.DCG_COMMON_ERROR);
LogUtils.error(LOGGER, errorLevel, "关闭websocket异常");
} finally {
webSocketServer.session = null;
}
}
}
}
/**
* 停止心跳检查
*/
private void stopPingTrigger() {
if (TIMER != null) {
TIMER.cancel();
}
}
/**
* 给指定用户发送消息.
*
* @param message 消息
* @param uniqueId 用户ID
* @throws IOException error
*/
public static void sendInfo(String message, @PathParam("uniqueId") String uniqueId)
throws IOException {
LogUtils.infoWithOutTChain(LOGGER, "发送消息到: {}, 报文: {}.", uniqueId, message);
if (StringUtils.isNotBlank(uniqueId) && WEB_SOCKET_CLIENT_LIST.containsKey(uniqueId)) {
WEB_SOCKET_CLIENT_LIST.get(uniqueId).sendMessage(message);
} else {
ErrorLevel errorLevel = ErrorLevel.buildErrorLevel1(DCGErrorCode.DCG_COMMON_ERROR);
LogUtils.error(LOGGER, errorLevel, "用户: {}, 不在线!", uniqueId);
}
}
}
1.6 调用
final String msg = MessageSourceUtils.getMessage("aaa.bbb.ccc");
PopUtils.sendMessagePop(msg);
1.7 结果
消息只弹出中文成功
。
2 结果排查与分析
我写了接口,接口里写了对该数据的国际化调用。前端调用该接口,弹框消息显示英文success
。说明我的国际化配置是正确的。
当使用WebSocket主动推送时,弹框消息显示中文成功
。
也,失败了。怎么回事???????
反复排查,搜索,论证。
真相只有一个!
经排查,是项目在使用MessageSource
的时候,后端主动推送弹框信息调用getMessage()方法时的Locale
传值为zh_CN
;当使用
接口调用的时候,Local
传值为en_US
。
为什么呢?
WebSocket 不是基于 HTTP 的,尽管它使用了 HTTP 的某些部分来建立初始连接。WebSocket 是一种网络通信协议,它提供了一个持久的、全双工的、基于 TCP 的连接,允许服务器和客户端之间进行实时的数据交换。
以下是 WebSocket 和 HTTP 之间的主要区别:
- 连接持久性:HTTP 是无状态的,每个请求都需要建立一个新的连接。而 WebSocket 在建立连接后,该连接会一直保持打开状态,直到一方主动关闭连接。
- 通信方式:HTTP 使用请求-响应模式进行通信,即客户端发送请求,服务器返回响应。而 WebSocket 提供了全双工的通信,即客户端和服务器都可以在任何时候发送消息。
- 协议头:WebSocket 的握手阶段使用了 HTTP 的部分协议头,但一旦连接建立,它就不再使用 HTTP。WebSocket 有自己的帧格式和编码方式。
- 实时性:由于 WebSocket 提供了持久的连接和实时的数据交换,它非常适合用于需要实时通信的应用,如在线聊天、股票行情、实时游戏等。而 HTTP 的实时性较差,因为它需要不断地发送请求和等待响应。
因此,虽然 WebSocket 在建立连接时使用了 HTTP 的部分机制,但它本身是一个独立的协议,不是基于 HTTP 的。
项目是在前端登录的时候选择语言环境,并将其放在请求头里,发送带有 Accept-Language
头部的请求,后端解析该请求,将en_US
作为Locale
传值,所以前端调用接口能实现国际化,消息弹框显示英文succsee
。
而使用WebSocket方式主动推送到前端,后端的Locale
传值始终是默认的zh_CN
,所以消息弹框显示中文成功
。
关键:解决此问题就是WebSocket方式主动推送到前端时,如何获取语言环境。
找到问题所在,就好解决了。
3 解决
3.1 WebSocketServer修改后
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Locale;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
@ServerEndpoint("/ws/{acceptLanguage}/{uniqueId}")
@Component
public class WebSocketServer {
private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketServer.class);
public static final String KEY = "DCG_FORE_HEARTBEAT";
/**
* concurrent包的线程安全Set, 用来存放每个客户端对应的MyWebSocket对象.
*/
private static final ConcurrentHashMap<String, WebSocketServer> WEB_SOCKET_CLIENT_LIST = new ConcurrentHashMap<>();
/**
* ping定时器
*/
private static final Timer TIMER;
/**
* 与某个客户端的连接会话, 需要通过它来给客户端发送数据.
*/
private Session session;
/**
* 接收uniqueId.
*/
private String uniqueId = "";
/**
* 用户选择的语言.
*/
private Locale locale = Locale.getDefault();
static {
TIMER = new Timer("websocket-ping-timer");
TIMER.schedule(new TimerTask() {
@Override
public void run() {
pingOpenConnections();
}
}, 1000, 60000);
}
/**
* sendHeartCheck:
* 发送检测心跳 <br/>
*/
private static void pingOpenConnections() {
if (!WEB_SOCKET_CLIENT_LIST.isEmpty()) {
WEB_SOCKET_CLIENT_LIST.forEach((id, websocketServer) -> {
try {
if (KEY.equalsIgnoreCase(id)) {
DcgForeStatusUtils.cacheDcgForeStatus(true);
}
websocketServer.sendPing();
} catch (Exception e) {
LogUtils.error(LOGGER,
ErrorLevel.buildErrorLevel1(DCGErrorCode.DCG_COMMON_ERROR),
"websocket user: {}, sendHeartCheckMsg error:!", id, e);
}
});
}
}
/**
* sendHeartCheckMsg:发送心跳消息 <br/>
* Description <br/>
*
* @throws IOException
* @throws IllegalArgumentException
*/
private void sendPing() throws IllegalArgumentException, IOException {
LogUtils.infoWithOutTChain(LOGGER, "websocket send PING to: {}", this.uniqueId);
if (this.session.isOpen()) {
this.session.getBasicRemote().sendPing(ByteBuffer.wrap(new byte[10]));
}
}
private Locale resolveLocale(String acceptLanguage) {
final Locale tempDefaultLocale = Locale.getDefault();
if (tempDefaultLocale != null && acceptLanguage == null) {
// Accept-Language为空时返回配置文件配置的默认语言
LogUtils.warn(LOGGER, "Http request header can not contains 'Accept-Language'.");
return tempDefaultLocale;
} else if (StringUtils.contains(acceptLanguage, "_")) {
final String[] split = acceptLanguage.split("_");
return new Locale(split[0], split[1]);
}
return new Locale("zh", "CN");
}
@OnOpen
public void onOpen(Session session, @PathParam("acceptLanguage") String acceptLanguage,
@PathParam("uniqueId") String uniqueId) {
this.session = session;
this.uniqueId = uniqueId;
this.locale = resolveLocale(acceptLanguage);
WEB_SOCKET_CLIENT_LIST.remove(uniqueId);
WEB_SOCKET_CLIENT_LIST.put(uniqueId, this);
LogUtils.infoWithOutTChain(LOGGER, "用户连接: {}, 当前连接数为: {}.", uniqueId, WEB_SOCKET_CLIENT_LIST.size());
try {
sendMessage("连接成功");
} catch (Exception e) {
ErrorLevel errorLevel = ErrorLevel.buildErrorLevel1(DCGErrorCode.DCG_COMMON_ERROR);
LogUtils.error(LOGGER, errorLevel, "用户: {},网络异常!!!!!!", uniqueId);
}
if (KEY.equalsIgnoreCase(uniqueId)) {
DcgForeStatusUtils.cacheDcgForeStatus(true);
}
}
@OnClose
public void onClose() {
LogUtils.infoWithOutTChain(LOGGER, "用户退出心跳: {}.", uniqueId);
WEB_SOCKET_CLIENT_LIST.remove(uniqueId);
if (KEY.equalsIgnoreCase(uniqueId)) {
DcgForeStatusUtils.cacheDcgForeStatus(false);
}
LogUtils.infoWithOutTChain(LOGGER, "用户退出: {}, 当前连接数为: {}.", uniqueId, WEB_SOCKET_CLIENT_LIST.size());
}
@OnMessage
public void onMessage(String message, Session session) {
LogUtils.infoWithOutTChain(LOGGER, "用户消息: {}, 报文: {}", this.uniqueId, message);
if (StringUtils.isNotBlank(message)) {
transportMessage(message);
}
}
/**
* 消息转发
*
* @param message 原消息
*/
private void transportMessage(String message) {
try {
final JSONObject jsonObject = JSON.parseObject(message);
jsonObject.put("fromUniqueId", this.uniqueId);
String toUniqueId = jsonObject.getString("toUniqueId");
if (StringUtils.isNotBlank(toUniqueId) && WEB_SOCKET_CLIENT_LIST.containsKey(toUniqueId)) {
WEB_SOCKET_CLIENT_LIST.get(toUniqueId).sendMessage(jsonObject.toJSONString());
} else {
ErrorLevel errorLevel = ErrorLevel.buildErrorLevel1(DCGErrorCode.DCG_COMMON_ERROR);
LogUtils.error(LOGGER, errorLevel, "请求的userId: {} 不在该服务器上.", toUniqueId);
}
} catch (Exception e) {
ErrorLevel errorLevel = ErrorLevel.buildErrorLevel1(DCGErrorCode.DCG_COMMON_ERROR);
LogUtils.error(LOGGER, errorLevel, "sendMessage failed.", e);
}
}
@OnError
public void onError(Session session, Throwable cause) {
ErrorLevel errorLevel = ErrorLevel.buildErrorLevel1(DCGErrorCode.DCG_COMMON_ERROR);
LogUtils.error(LOGGER, errorLevel, "用户错误: {}. 发送信息失败: {}", this.uniqueId, cause);
}
public void sendMessage(String message) throws IOException {
if (this.session != null && this.session.isOpen()) {
synchronized (this.session) {
this.session.getBasicRemote().sendText(message);
}
}
}
/**
* 关闭所有ws服务
*/
public void closeWebSocketServer() {
stopPingTrigger();
for (Map.Entry<String, WebSocketServer> server : WEB_SOCKET_CLIENT_LIST.entrySet()) {
WebSocketServer webSocketServer = server.getValue();
Session tempSession = webSocketServer.session;
if (null != tempSession) {
try {
tempSession.close();
} catch (Exception e) {
ErrorLevel errorLevel = ErrorLevel.buildErrorLevel1(DCGErrorCode.DCG_COMMON_ERROR);
LogUtils.error(LOGGER, errorLevel, "关闭websocket异常");
} finally {
webSocketServer.session = null;
}
}
}
}
/**
* 停止心跳检查
*/
private void stopPingTrigger() {
if (TIMER != null) {
TIMER.cancel();
}
}
public Locale getAcceptLanguage() {
if (this.session != null && this.session.isOpen()) {
return this.locale;
}
return Locale.getDefault();
}
/**
* 给指定用户发送消息.
*
* @param message 消息
* @param uniqueId 用户ID
* @throws IOException error
*/
public static void sendInfo(String message, @PathParam("uniqueId") String uniqueId)
throws IOException {
LogUtils.infoWithOutTChain(LOGGER, "发送消息到: {}, 报文: {}.", uniqueId, message);
if (StringUtils.isNotBlank(uniqueId) && WEB_SOCKET_CLIENT_LIST.containsKey(uniqueId)) {
WEB_SOCKET_CLIENT_LIST.get(uniqueId).sendMessage(message);
} else {
ErrorLevel errorLevel = ErrorLevel.buildErrorLevel1(DCGErrorCode.DCG_COMMON_ERROR);
LogUtils.error(LOGGER, errorLevel, "用户: {}, 不在线!", uniqueId);
}
}
/**
* 获取用户选择的语言.
*
* @param uniqueId id
* @return Locale
*/
public static Locale getAcceptLanguage(String uniqueId) {
if (StringUtils.isNotBlank(uniqueId) && WEB_SOCKET_CLIENT_LIST.containsKey(uniqueId)) {
return WEB_SOCKET_CLIENT_LIST.get(uniqueId).getAcceptLanguage();
}
ErrorLevel errorLevel = ErrorLevel.buildErrorLevel1(DCGErrorCode.DCG_COMMON_ERROR);
LogUtils.error(LOGGER, errorLevel, "用户: {}, 不在线!", uniqueId);
return Locale.getDefault();
}
}
3.2 MessageSourceUtils添加getMessage4WS方法
public final class MessageSourceUtils {
private static final MessageSource MESSAGE_SOURCE = SpringContextHolder.getBean(MessageSource.class);
/**
* default constructor.
*/
private MessageSourceUtils() {
}
/**
* getMessage.
*
* @param key key
* @return value
*/
public static String getMessage(String key) {
return MESSAGE_SOURCE.getMessage(key, null, LocaleContextHolder.getLocale());
}
/**
* getMessage4WS.
*
* @param key String
* @param locale Locale
* @return String
*/
public static String getMessage4WS(String key, Locale locale) {
return MESSAGE_SOURCE.getMessage(key, null, locale);
}
3.3 调用
String msg = MessageSourceUtils.getMessage4WS("aaa.bbb.ccc",WebSocketServer.getAcceptLanguage("DEVICE_MESSAGE_POP"));
WebSocketServer.sendInfo(JSON.toJSONString(R.ok(format)), "DEVICE_MESSAGE_POP");
3.4 前端
对应前端调用WebSocket路径也要修改
/*
*
* @param key key
* @return value
*/
public static String getMessage(String key) {
return MESSAGE_SOURCE.getMessage(key, null, LocaleContextHolder.getLocale());
}
/**
* getMessage4WS.
*
* @param key String
* @param locale Locale
* @return String
*/
public static String getMessage4WS(String key, Locale locale) {
return MESSAGE_SOURCE.getMessage(key, null, locale);
}
3.3 调用
String msg = MessageSourceUtils.getMessage4WS("aaa.bbb.ccc",WebSocketServer.getAcceptLanguage("DEVICE_MESSAGE_POP"));
WebSocketServer.sendInfo(JSON.toJSONString(R.ok(format)), "DEVICE_MESSAGE_POP");
3.4 前端
对应前端调用WebSocket路径也要修改
至此,完美解决。