Java for Web学习笔记(七六):国际化i18n(4)其他

在js中动态给出

在某种情况下,例如在websocket子中,我们根据js收到的websocket状态变化等向用户进行本地化显示,也就是说本地化不是在一开始render的时候进行的,而是在js中动态给出。小例子中infoMessage是在某个Area中增加信息。
var infoMessage = function(m) {
      chatLog.append($('<div>').addClass('informational').text(moment().format('h:mm:ss a') + ': ' + m));
};

<!-- 例子1:无参数的本地化情况 -->
infoMessage('<spring:message code="message.chat.connecting" javaScriptEscape="true" />');

<!-- 例子2:带参数的本地化情况:通过replace进行替换 -->
infoMessage('<spring:message code="message.chat.joined" javaScriptEscape="true"/>'.replace('{0}', message.user));

一个在websocket中的例子

在websocket对话的例子中,server除了向client转发用户的对话内容,也会向client发送一些信息,例如你加入了那个会话室,有谁加入会话,有谁推出会话,这些信息是通过websocket发送的,我们希望能对这部分信息进行本地化,需要:

  1. 在websocket握手过程中获取用户的locale,以便在后面进行本地化
  2. 这不是必要的,可以有其他的方式,但却很有趣
    • 将本地化中的信息代码contentCode,相关的参数contentArgument,也放在ChatMessage中,而ChatMessage在websocket中以json二进制的方式传递。需要告知websocket的decoder,这两项不要放入json中。(当然你可以全局都是@JsonIgnore,但假如只是websocket的decoder不需要)
    • 在消息中放入content和localizedContent,如果是通知类的消息,将本地化后,写入content,如果是用户之间的对话,将放入content,无需本地化。

ObjectMapper中屏蔽某些json信息

我们先看看websocket传递的消息ChatMessage,支持clone。
public class ChatMessage implements Cloneable{
    private Instant timestamp;
    private Type type;
    private String user;
    private String contentCode;         //这是本地化所需,我们希望在websocket的json decoder中不处理这信息
    private Object[] contentArguments;  //这是本地化所需,我们希望在websocket的json decoder中不处理这信息
    private String localizedContent;
    private String userContent;

    //...getters and setters

    @Override
    @SuppressWarnings("CloneDoesntDeclareCloneNotSupportedException")
    protected ChatMessage clone() {
        try {
            return (ChatMessage)super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException("Impossible clone not supported.", e);
        }
    }

    /* 对于webSocket中,我们希望在websocket的json decoder中不处理这信息contentCode和contentArguments。
     * 也就是希望怎对websocket的decoder,在这四个getter和setter中加上@JsonIgnore
     * 我们先定义一个抽象类,在后面在websocket的decoder时这些方法增加annotation @JsonIgnore  */
    static abstract class MixInForWebSocket
    {
        @JsonIgnore public abstract String getContentCode();
        @JsonIgnore public abstract void setContentCode(String c);
        @JsonIgnore public abstract Object[] getContentArguments();
        @JsonIgnore public abstract void setContentArguments(Object... c);
    }

    /* 我们希望在log中屏蔽掉localizedContent(和websocket中传递消息屏蔽的不一样,所以不能用全局的@JsonIgnore)
     * 我们同样也定义一个抽象类 */
    static abstract class MixInForLogWrite
    {
        @JsonIgnore public abstract String getLocalizedContent();
        @JsonIgnore public abstract void setLocalizedContent(String l);
    }
}
在进行chat服务的DefaultChatService中我们将记录通话的log,这里我们希望屏蔽localizedContent。
@Service
public class DefaultChatService implements ChatService{    
    @Inject ObjectMapper objectMapper; 

    //【学习1】对于spring,支持@PostConstruct标记,表明在构造后执行。另一个比较常用的是@PreDestroy
    @PostConstruct
    public void initialize(){
        /* 下面是javadoc的说明,我们要注意到,这里使用的objectMapper Bean,Bean是全局唯一的。
         * Method to use for adding mix-in annotations to use for augmenting specified class or interface.
         *  All annotations from mixinSource are taken to override annotations 
         *  that target (or its supertypes) has.
         *  Parameters:
         *  target      Class (or interface) whose annotations to effectively override
         *  mixinSource Class (or interface) whose annotations are to be "added" to target's annotations, 
         *              overriding as necessary
         *  */
         this.objectMapper.addMixIn(ChatMessage.class, ChatMessage.MixInForLogWrite.class);
    }   
}
我们再看看websocket的decoder
@ServerEndpoint(value = "/chat/{sessionId}",
    encoders = ChatMessageCodec.class,
    decoders = ChatMessageCodec.class,
    configurator = ChatEndpoint.EndpointConfigurator.class)
public class ChatEndpoint {
  ... ...
}

public class ChatMessageCodec implements Encoder.BinaryStream<ChatMessage>,Decoder.BinaryStream<ChatMessage>{
    //这里没有使用bean,是另一个json解析器或封装器。ChatMessageCodec不属于spring框架,不能使用bean。
    private static final ObjectMapper MAPPER = new ObjectMapper();

    static {
        MAPPER.findAndRegisterModules();
        MAPPER.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false);
        //同样的,通过addMinIn(),告知这个解析器Ignore相关的信息
        MAPPER.addMixIn(ChatMessage.class,ChatMessage.MixInForWebSocket.class);
    }
}

在webscoket的握手阶段获取locale

websocket的握手阶段是HTTP,可以在此获取locale并存放在websocket session的properties中。
@ServerEndpoint(value = "/chat/{sessionId}",
    encoders = ChatMessageCodec.class,
    decoders = ChatMessageCodec.class,
    configurator = ChatEndpoint.EndpointConfigurator.class)
public class ChatEndpoint {
    ... ...

    public static class EndpointConfigurator extends SpringConfigurator/*ServerEndpointConfig.Configurator*/{
        private static final String LOCALE_KEY = "com.wrox.ws.user.locale";

        @Override
        public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
            super.modifyHandshake(config, request, response);
            ... ...
            config.getUserProperties().put(LOCALE_KEY, LocaleContextHolder.getLocale());
        }

        private static Locale getExposedLocale(Session session){ //增加一个静态方法方便读取
            return (Locale) session.getUserProperties().get(LOCALE_KEY);
        }        
    }
}
websocket发送带有本地化信息的消息
    session.getBasicRemote().sendObject(this.cloneAndLocalize(result.getCreateMessage(),this.locale));

    private ChatMessage cloneAndLocalize(ChatMessage message, Locale locale){
        message = message.clone();
        message.setLocalizedContent(
            this.messageSource.getMessage(message.getContentCode(), message.getContentArguments(), locale));
        return message;
    }
相应的在js中,如果content有内容显示content,无则显示localizedContent。

相关文章相关链接: 我的Professional Java for Web Applications相关文章


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值