后端使用WebSocket主动向前端推送数据,该数据做国际化时,只显示中文的一种解决方式

近期,项目中碰到一个问题:使用WebSocket向前端推送一个弹框信息,弹框信息会根据语言环境不同显示不同语言。但是在使用i18n做国际化的时候,只能显示中文。

1 修改之前

1.1 国际化配置

首先,国际化配置是做正确了的。

java服务中接收前端弹窗信息_java服务中接收前端弹窗信息

该文件里的值也是正确的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 之间的主要区别:

  1. 连接持久性:HTTP 是无状态的,每个请求都需要建立一个新的连接。而 WebSocket 在建立连接后,该连接会一直保持打开状态,直到一方主动关闭连接。
  2. 通信方式:HTTP 使用请求-响应模式进行通信,即客户端发送请求,服务器返回响应。而 WebSocket 提供了全双工的通信,即客户端和服务器都可以在任何时候发送消息。
  3. 协议头:WebSocket 的握手阶段使用了 HTTP 的部分协议头,但一旦连接建立,它就不再使用 HTTP。WebSocket 有自己的帧格式和编码方式。
  4. 实时性:由于 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路径也要修改

至此,完美解决。