使用过滤器实现后台返回Response国际化
前言 :
写这篇文档之前,其实我看过了spring的国际化处理,使用spring去处理国际化也确实方便,但由于公司项目是已经做好了的,只有一个中文版,如果直接改成用spring的话,需要改的代码量非常大,所以我就想着根据自己的项目,然后模仿spring的做法去实现国际化。
一、前端
现在我们做的项目是前后端分离的,也就是后端的请求全部返回JSON数据,前端全部使用Ajax去处理后台返回的数据,所以前端页面的国际化实现,实际上就是copy一份中文版的项目,然后把页面上的中文全部改成英文的,这种做法实在是最蠢的,如果以后需要添加更多的语言支持,岂不是要copy好几份出来,所以这种做法是不可取的,虽然我们是用的这种做法。更好的方法是前端也使用国际化框架,这里比较常用的就是 jquery.i18n.properties的使用讲解与实例 这个了,有兴趣的可以看看,本文主要讲的是后台的实现方法。
二、后台
1、思路
我们模仿spring的做法,定义两套properties文件,一个中文一个英文,然后通过前端传过来的请求头language
去判断是中文版(zh-cn
)的前端访问还是英文版(en-us
)的前端在访问, 后台使用一个过滤器过滤response,实际上就是动态去加载properties中的value去替换response中的返回值。
2、返回值
后台的返回值是用自定义的一个对象封装起来的,仅作参考。 代码如下:
package com.blog.www.bean.common;
import lombok.*;
import org.springframework.context.annotation.Scope;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
/**
* 响应对象。包含处理结果(Meta)和返回数据(Data)两部分,在 Controller 处理完请求后将此对象转换成 json 返回给前台。注意:
* <ul>
* <li>处理成功一般返回处理结果和返回数据,失败只返回处理结果。具体返回什么需看接口文档。</li>
* <li>处理成功结果码一般是200,失败码具体看出了什么错,对照 HTTP 响应码填。</li>
* <li>默认处理方法慎用,前台最想要拿到的还是具体的结果码和信息。</li>
* </ul>
* <p>
* @author :leigq <br>
* 创建时间:2017年10月9日 下午3:26:17 <br>
* <p>
* 修改人: <br>
* 修改时间: <br>
* 修改备注: <br>
* </p>
*/
@Component
@Scope("prototype")
@SuppressWarnings(value = "all")
@AllArgsConstructor
@NoArgsConstructor
public class Response {
/**
* 默认成功响应码
*/
private static final Integer DEFAULT_SUCCESS_CODE = HttpStatus.OK.value();
/**
* 默认成功响应信息
*/
private static final String DEFAULT_SUCCESS_MSG = "请求/处理成功!";
/**
* 默认国际化成功响应信息
*/
private static final String DEFAULT_I18N_SUCCESS_MSG = "REQUEST_SUCCESS";
/**
* 默认失败响应码
*/
private static final Integer DEFAULT_FAILURE_CODE = HttpStatus.INTERNAL_SERVER_ERROR.value();
/**
* 默认失败响应信息
*/
private static final String DEFAULT_FAILURE_MSG = "请求/处理失败!";
/**
* 默认国际化失败响应信息
*/
private static final String DEFAULT_I18N_FAILURE_MSG = "REQUEST_FAIL";
@Getter
@Setter
private Meta meta;
@Getter
@Setter
private Object data;
/*******处理成功响应*************************************************************************************/
/*↓↓↓↓↓↓默认(200)响应码,默认信息,无返回数据↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/
/**
* 处理成功响应,默认(200)响应码,默认信息,无返回数据
*
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:25 <br>
*/
public Response success() {
this.meta = new Meta(DEFAULT_SUCCESS_CODE, DEFAULT_SUCCESS_MSG, false, null);
this.data = null;
return this;
}
/**
* 处理国际化成功响应,默认(200)响应码,默认信息,无返回数据
*
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:25 <br>
*/
public Response successI18n() {
this.meta = new Meta(DEFAULT_SUCCESS_CODE, DEFAULT_I18N_SUCCESS_MSG, true, null);
this.data = null;
return this;
}
/*↓↓↓↓↓↓默认(200)响应码,自定义信息,无返回数据↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/
/**
* 处理成功响应,默认(200)响应码,自定义信息,无返回数据
*
* @param msg 处理结果信息
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:25 <br>
*/
public Response success(String msg) {
this.meta = new Meta(DEFAULT_SUCCESS_CODE, msg, false, null);
this.data = null;
return this;
}
/**
* 处理成功响应,默认(200)响应码,自定义信息,无返回数据
*
* @param msg 处理结果信息
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:25 <br>
*/
public Response successI18n(String msg) {
this.meta = new Meta(DEFAULT_SUCCESS_CODE, msg, true, null);
this.data = null;
return this;
}
/**
* 处理成功响应,默认(200)响应码,自定义信息,无返回数据
*
* @param msg 处理结果信息
* @param msgParams 结果信息参数
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:25 <br>
*/
public Response successI18n(String msg, Object[] msgParams) {
this.meta = new Meta(DEFAULT_SUCCESS_CODE, msg, true, msgParams);
this.data = null;
return this;
}
/*↓↓↓↓↓↓默认(200)响应码,默认信息,有返回数据。↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/
/**
* 处理成功响应,默认(200)响应码,默认信息,有返回数据。
*
* @param data 返回数据
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:25 <br>
*/
public Response success(Object data) {
this.meta = new Meta(DEFAULT_SUCCESS_CODE, DEFAULT_SUCCESS_MSG, false, null);
this.data = data;
return this;
}
/**
* 处理成功响应,默认(200)响应码,默认信息,有返回数据。
*
* @param data 返回数据
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:25 <br>
*/
public Response successI18n(Object data) {
this.meta = new Meta(DEFAULT_SUCCESS_CODE, DEFAULT_I18N_SUCCESS_MSG, true, null);
this.data = data;
return this;
}
/*↓↓↓↓↓↓默认(200)响应码,自定义信息,有返回数据↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/
/**
* 处理成功响应,默认(200)响应码,自定义信息,有返回数据
*
* @param msg 处理结果信息
* @param data 返回数据
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:25 <br>
*/
public Response success(String msg, Object data) {
this.meta = new Meta(DEFAULT_SUCCESS_CODE, msg, false, null);
this.data = data;
return this;
}
/**
* 处理成功响应,默认(200)响应码,自定义信息,有返回数据
*
* @param msg 处理结果信息
* @param data 返回数据
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:25 <br>
*/
public Response successI18n(String msg, Object data) {
this.meta = new Meta(DEFAULT_SUCCESS_CODE, msg, true, null);
this.data = data;
return this;
}
/**
* 处理成功响应,默认(200)响应码,自定义信息,有返回数据
*
* @param msg 处理结果信息
* @param data 返回数据
* @param msgParams 结果信息参数
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:25 <br>
*/
public Response successI18n(String msg, Object data, Object[] msgParams) {
this.meta = new Meta(DEFAULT_SUCCESS_CODE, msg, true, msgParams);
this.data = data;
return this;
}
/*↓↓↓↓↓↓自定义响应码,自定义信息,有返回数据↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/
/**
* 处理成功响应,自定义响应码,自定义信息,有返回数据
*
* @param httpStatus HTTP 响应码
* @param msg 处理结果信息
* @param data 返回数据
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:25 <br>
*/
public Response success(HttpStatus httpStatus, String msg, Object data) {
this.meta = new Meta(httpStatus.value(), msg, false, null);
this.data = data;
return this;
}
/**
* 处理成功响应,自定义响应码,自定义信息,有返回数据
*
* @param httpStatus HTTP 响应码
* @param msg 处理结果信息
* @param data 返回数据
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:25 <br>
*/
public Response successI18n(HttpStatus httpStatus, String msg, Object data) {
this.meta = new Meta(httpStatus.value(), msg, true, null);
this.data = data;
return this;
}
/**
* 处理成功响应,自定义响应码,自定义信息,有返回数据
*
* @param httpStatus HTTP 响应码
* @param msg 处理结果信息
* @param data 返回数据
* @param msgParams 结果信息参数
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:25 <br>
*/
public Response successI18n(HttpStatus httpStatus, String msg, Object data, Object[] msgParams) {
this.meta = new Meta(httpStatus.value(), msg, true, msgParams);
this.data = data;
return this;
}
/*******处理失败响应*************************************************************************************/
/*↓↓↓↓↓↓默认(500)响应码,默认信息,无返回数据↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/
/**
* 处理失败响应,返回默认(500)响应码、默认信息,无返回数据。
*
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:22 <br>
*/
public Response failure() {
this.meta = new Meta(DEFAULT_FAILURE_CODE, DEFAULT_FAILURE_MSG, false, null);
this.data = null;
return this;
}
/**
* 处理国际化失败响应,返回默认(500)响应码、默认信息,无返回数据。
*
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:22 <br>
*/
public Response failureI18n() {
this.meta = new Meta(DEFAULT_FAILURE_CODE, DEFAULT_I18N_FAILURE_MSG, true, null);
this.data = null;
return this;
}
/*↓↓↓↓↓↓默认(500)响应码,自定义信息,无返回数据↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/
/**
* 处理失败响应,返回默认(500)响应码、自定义信息,无返回数据。
*
* @param msg 处理结果信息
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:22 <br>
*/
public Response failure(String msg) {
this.meta = new Meta(DEFAULT_FAILURE_CODE, msg, false, null);
this.data = null;
return this;
}
/**
* 处理国际化失败响应,返回默认(500)响应码、自定义信息,无返回数据。
*
* @param msg 处理结果信息
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:22 <br>
*/
public Response failureI18n(String msg) {
this.meta = new Meta(DEFAULT_FAILURE_CODE, msg, true, null);
this.data = null;
return this;
}
/**
* 处理国际化失败响应,返回默认(500)响应码、自定义信息,无返回数据。
*
* @param msg 处理结果信息
* @param msgParams 结果信息参数
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:22 <br>
*/
public Response failureI18n(String msg, Object[] msgParams) {
this.meta = new Meta(DEFAULT_FAILURE_CODE, msg, true, msgParams);
this.data = null;
return this;
}
/*↓↓↓↓↓↓默认(500)响应码,默认信息,有返回数据。↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/
/**
* 处理失败响应,默认(500)响应码,默认信息,有返回数据。
*
* @param data 返回数据
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:22 <br>
*/
public Response failure(Object data) {
this.meta = new Meta(DEFAULT_FAILURE_CODE, DEFAULT_FAILURE_MSG, false, null);
this.data = data;
return this;
}
/**
* 处理国际化失败响应,默认(500)响应码,默认信息,有返回数据。
*
* @param data 返回数据
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:22 <br>
*/
public Response failureI18n(Object data) {
this.meta = new Meta(DEFAULT_FAILURE_CODE, DEFAULT_I18N_FAILURE_MSG, true, null);
this.data = data;
return this;
}
/*↓↓↓↓↓↓默认(500)响应码,自定义信息,有返回数据↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/
/**
* 处理失败响应,默认(500)响应码,自定义信息,有返回数据
*
* @param msg 处理结果信息
* @param data 返回数据
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:22 <br>
*/
public Response failure(String msg, Object data) {
this.meta = new Meta(DEFAULT_FAILURE_CODE, msg, false, null);
this.data = data;
return this;
}
/**
* 处理国际化失败响应,默认(500)响应码,自定义信息,有返回数据,有结果信息参数。
*
* @param msg 处理结果信息
* @param data 返回数据
* @param msgParams 结果信息参数
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:22 <br>
*/
public Response failureI18n(String msg, Object data, Object[] msgParams) {
this.meta = new Meta(DEFAULT_FAILURE_CODE, msg, true, msgParams);
this.data = data;
return this;
}
/**
* 处理国际化失败响应,默认(500)响应码,自定义信息,有返回数据,无结果信息参数。
*
* @param msg 处理结果信息
* @param data 返回数据
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:22 <br>
*/
public Response failureI18n(String msg, Object data) {
this.meta = new Meta(DEFAULT_FAILURE_CODE, msg, true, null);
this.data = data;
return this;
}
/*↓↓↓↓↓↓自定义响应码,自定义信息,有返回数据↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/
/**
* 处理失败响应,自定义响应码,自定义信息,有返回数据
*
* @param httpStatus HTTP 响应码
* @param msg 处理结果信息
* @param data 返回数据
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:22 <br>
*/
public Response failure(HttpStatus httpStatus, String msg, Object data) {
this.meta = new Meta(httpStatus.value(), msg, false, null);
this.data = data;
return this;
}
/**
* 处理国际化失败响应,自定义响应码,自定义信息,有返回数据,有结果信息参数。
*
* @param httpStatus HTTP 响应码
* @param msg 处理结果信息
* @param data 返回数据
* @param msgParams 结果信息参数
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:22 <br>
*/
public Response failureI18n(HttpStatus httpStatus, String msg, Object data, Object[] msgParams) {
this.meta = new Meta(httpStatus.value(), msg, true, msgParams);
this.data = data;
return this;
}
/**
* 处理国际化失败响应,自定义响应码,自定义信息,有返回数据,无结果信息参数。
*
* @param httpStatus HTTP 响应码
* @param msg 处理结果信息
* @param data 返回数据
* @return 响应对象
* <p>
* @author :LeiGQ <br>
* @date :2019-05-20 15:22 <br>
*/
public Response failureI18n(HttpStatus httpStatus, String msg, Object data) {
this.meta = new Meta(httpStatus.value(), msg, true, null);
this.data = data;
return this;
}
/**
* 元数据,包含响应码和信息。
* <p>
* 创建人:leigq <br>
* 创建时间:2017年10月9日 下午3:31:17 <br>
* <p>
* 修改人: <br>
* 修改时间: <br>
* 修改备注: <br>
* </p>
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Meta {
/**
* 处理结果代码,与 HTTP 状态响应码对应
*/
private Integer code;
/**
* 处理结果信息
*/
private String msg;
/**
* 处理结果信息是否国际化
*/
private Boolean isI18n;
/**
* 处理结果信息参数
*/
private Object[] msgParams;
}
}
后台调用的时候是这样的:
最后前端显示:
还有一种情况是,提示语中的信息可能是动态填充进去的,像下面这样,那么,我们就可以使用 {}
来进行占位, 填充数据使用数组装起来即可。
3、properties文件
定义两套properties文件,用来存放国际化的字符串,language_zh_CN.properties
[中文]和language_en_US.properties
[英文],像下面这样:
这里需要注意的是,俩个properties文件的key要保持一致,以确保能正常取值。
4、过滤器
过滤器不会用的看这里:Java过滤器Filter使用详解
过滤器直接看代码,注释很详细,这里我的项目使用的是SpringBoot,所以就直接用注解定义过滤器了
package com.blog.www.web;
import com.blog.www.bean.common.Response;
import com.blog.www.bean.i18n.CnI18nPropertiesStrategy;
import com.blog.www.bean.i18n.EnI18nPropertiesStrategy;
import com.blog.www.bean.i18n.I18nPropertiesStrategy;
import com.blog.www.bean.i18n.I18nPropertiesStrategyContext;
import com.blog.www.util.JSONUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;
/**
* 实现国际化过滤器
* <p>通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。
* 例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。
* 使用Filter的完整流程:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
* <br/>
* 具体如何实现国际化,参考:<a href='http://note.youdao.com/noteshare?id=53637753599255d98737bfc4060cb4b7'>使用过滤器实现后台返回Response国际化</a>
* 创建人:leigq <br>
* 创建时间:2018-11-05 11:43 <br>
* <p>
* 修改人: <br>
* 修改时间: <br>
* 修改备注: <br>
* </p>
*/
@WebFilter(filterName = "响应国际化过滤器", urlPatterns = "/*")
// 标注这是一个过滤器,属性filterName声明过滤器的名称(可选);属性urlPatterns指定要过滤的URL模式,也可使用属性value来声明(指定要过滤的URL模式是必选属性)
@Order(Ordered.HIGHEST_PRECEDENCE)// 控制加载顺序,Ordered.HIGHEST_PRECEDENCE最高优先级
@Slf4j
public class I18NFilter implements Filter {
@Autowired
private Map<String, I18nPropertiesStrategy> i18nPropertiesStrategyMap;
/**
* 前端国际化请求头 key
*/
private static final String LANGUAGE_HEADER = "Language";
/*
* 前端请求头 value
* */
// 中文
private static final String ZH_CN = "zh-cn";
// 英文
private static final String EN_US = "en-us";
/**
* 用于保存请求头中的语言参数
*/
private String language;
@Override
public void init(FilterConfig filterConfig) {
log.warn("国际化过滤器-init...");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
log.info(">>>>>>>>>>请求进入国际化过滤器<<<<<<<<<");
HttpServletResponse resp = (HttpServletResponse) response;
HttpServletRequest req = (HttpServletRequest) request;
// 获取请求头中的语言参数
language = req.getHeader(LANGUAGE_HEADER);
if (StringUtils.isNotBlank(language)) {
handleResponse(request, response, resp, chain);
} else {
log.info(">>>>>> 国际化过滤器不做处理 <<<<<<");
try {
response.setCharacterEncoding("UTF-8");
chain.doFilter(request, response);
} catch (Exception e) {
log.info("处理国际化返回结果失败", e);
}
}
}
/**
* 处理响应
*/
private void handleResponse(ServletRequest request, ServletResponse response, HttpServletResponse resp, FilterChain chain) {
// 包装响应对象 resp 并缓存响应数据
ResponseWrapper mResp = new ResponseWrapper(resp);
ServletOutputStream out = null;
try {
out = response.getOutputStream();
// 防止出现乱码
mResp.setCharacterEncoding("UTF-8");
chain.doFilter(request, mResp);
// 获取缓存的响应数据
byte[] bytes = mResp.getBytes();
// 响应字符串
String responseStr = new String(bytes, "UTF-8");
// 将 String 类型响应数据转成 Response 对象
Response returnResponse = JSONUtils.json2pojo(responseStr, Response.class);
// meta 对象
Response.Meta meta = returnResponse.getMeta();
// 返回信息
String msg = meta.getMsg();
if (meta.getIsI18n()) {
// 返回信息参数
Object[] msgParams = meta.getMsgParams();
// 处理国际化
if (Objects.isNull(msgParams)) {
// 直接用 value 替换 key
responseStr = responseStr.replace(msg, getI18nVal(msg));
} else {
// 循环用 value 替换 key
String[] keys = msg.split("\\{}");
for (String key : keys) {
responseStr = responseStr.replaceFirst(key, getI18nVal(key));
}
// 循环处理返回结果参数
for (Object param : msgParams) {
responseStr = responseStr.replaceFirst("\\{}", param.toString());
}
}
}
out.write(responseStr.getBytes());
} catch (Exception e) {
log.error("处理国际化返回结果失败", e);
} finally {
try {
assert out != null;
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 根据properties文件中属性的key获取对应的值
* 说明:
* <p>
* 创建人: LGQ <br>
* 创建时间: 2018年8月13日 下午8:10:20 <br>
* <p>
* 修改人: <br>
* 修改时间: <br>
* 修改备注: <br>
* </p>
*/
private String getI18nVal(String langKey) {
I18nPropertiesStrategy i18nPropertiesStrategy;
switch (language) {
case ZH_CN:
i18nPropertiesStrategy = i18nPropertiesStrategyMap.get("cnI18nPropertiesStrategy");
break;
case EN_US:
i18nPropertiesStrategy = i18nPropertiesStrategyMap.get("enI18nPropertiesStrategy");
break;
default:
i18nPropertiesStrategy = i18nPropertiesStrategyMap.get("cnI18nPropertiesStrategy");
break;
}
String value = i18nPropertiesStrategy.getValue(langKey);
log.info("I18N Filter ### key = {} ----> value = {}", langKey, value);
return value;
}
@Override
public void destroy() {
log.warn("国际化过滤器-destroy...");
}
}
上面的过滤器中使用到了下面的几个类。
ResponseWrapper.java
package com.blog.www.web;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.*;
public class ResponseWrapper extends HttpServletResponseWrapper {
private ByteArrayOutputStream bytes = new ByteArrayOutputStream();
private PrintWriter pwrite;
ResponseWrapper(HttpServletResponse response) {
super(response);
}
@Override
public ServletOutputStream getOutputStream() {
// 将数据写到 byte 中
return new MyServletOutputStream(bytes);
}
/**
* 重写父类的 getWriter() 方法,将响应数据缓存在 PrintWriter 中
*/
@Override
public PrintWriter getWriter() {
try {
pwrite = new PrintWriter(new OutputStreamWriter(bytes, "utf-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return pwrite;
}
/**
* 获取缓存在 PrintWriter 中的响应数据
*/
byte[] getBytes() {
if (null != pwrite) {
pwrite.close();
return bytes.toByteArray();
}
if (null != bytes) {
try {
bytes.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
assert bytes != null;
return bytes.toByteArray();
}
class MyServletOutputStream extends ServletOutputStream {
private ByteArrayOutputStream ostream;
MyServletOutputStream(ByteArrayOutputStream ostream) {
this.ostream = ostream;
}
@Override
public void write(int b) {
// 将数据写到 stream 中
ostream.write(b);
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setWriteListener(WriteListener writeListener) {
}
}
}
I18nPropertiesStrategy.java
package com.blog.www.bean.i18n;
/**
* 国际化属性文件策略
* <br/>
* @author :leigq
* @date :2019/7/24 13:04
*/
public interface I18nPropertiesStrategy {
String getValue(String key);
}
CnI18nPropertiesStrategy.java
package com.blog.www.bean.i18n;
import com.blog.www.util.PropertiesUtils;
import java.util.Objects;
/**
* 中国国际化属性文件策略
* <br/>
*
* @author :leigq
* @date :2019/7/24 13:04
*/
@Component
public class CnI18nPropertiesStrategy implements I18nPropertiesStrategy {
private volatile static PropertiesUtils propertiesUtils;
@Override
public String getValue(String key) {
return getPropertiesUtils().getValue(key);
}
private PropertiesUtils getPropertiesUtils() {
if (Objects.isNull(propertiesUtils)) {
synchronized (CnI18nPropertiesStrategy.class) {
if (Objects.isNull(propertiesUtils)) {
propertiesUtils = PropertiesUtils.init("i18n/language_zh_CN.properties");
}
}
}
return propertiesUtils;
}
}
EnI18nPropertiesStrategy.java
package com.blog.www.bean.i18n;
import com.blog.www.util.PropertiesUtils;
import java.util.Objects;
/**
* 英文国际化属性文件策略
* <br/>
*
* @author :leigq
* @date :2019/7/24 13:04
*/
@Component
public class EnI18nPropertiesStrategy implements I18nPropertiesStrategy {
private volatile static PropertiesUtils propertiesUtils;
@Override
public String getValue(String key) {
return getPropertiesUtils().getValue(key);
}
private PropertiesUtils getPropertiesUtils() {
if (Objects.isNull(propertiesUtils)) {
synchronized (EnI18nPropertiesStrategy.class) {
if (Objects.isNull(propertiesUtils)) {
propertiesUtils = PropertiesUtils.init("i18n/language_en_US.properties");
}
}
}
return propertiesUtils;
}
}
PropertiesUtils.java
package com.blog.www.util;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import java.io.IOException;
import java.util.Objects;
import java.util.Properties;
/**
* 读取Properties文件工具类
* <br>
* 参考:<a href='https://note.youdao.com/share/?id=1a9228ce7c45ad383921eccf6c0580f2&type=note#/'>spring中如何读取.properties配置文件</a>
* <p>
* 创建人:leigq <br>
* 创建时间:2018-11-09 13:17 <br>
* <p>
* 修改人: <br>
* 修改时间: <br>
* 修改备注: <br>
* </p>
*/
public class PropertiesUtils {
private Logger log = LoggerFactory.getLogger(PropertiesUtils.class);
private String path;
private Properties properties;
private PropertiesUtils(String path) {
this.path = path;
try {
this.properties = PropertiesLoaderUtils.loadProperties(new ClassPathResource(path));
} catch (IOException e) {
log.error(String.format("地址为 %s 的文件不存在", path), e);
}
}
/**
* 构建PropertiesUtil
* <br>创建人: leigq
* <br>创建时间: 2018-11-09 13:56
* <br>
*
* @param path 资源文件路径,如:i18n/zh_CN.properties
*/
public static PropertiesUtils init(String path) {
return new PropertiesUtils(path);
}
/**
* 获取配置文件中的值
* <br>创建人: leigq
* <br>创建时间: 2018-11-09 14:05
* <br>
*
* @param key 键
*/
public String getValue(String key) {
if (StringUtils.isBlank(key)) {
throw new NullPointerException(String.format("配置文件 %s 中找不到这个Key,key = %s", path, key));
}
key = key.trim();
String property = properties.getProperty(key);
if (StringUtils.isBlank(property)) {
throw new NullPointerException(String.format("配置文件 %s 中 key = %s 的 value 为空", path, key));
}
return property.trim();
}
}
JSONUtils.java
package com.blog.www.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Jackson 工具类
* </br>
* 参考:https://www.cnblogs.com/xiezhenwei/p/3616576.html
* <p>
* 创建人:LeiGQ <br>
* 创建时间:2019-05-10 15:44 <br>
* <p>
* 修改人: <br>
* 修改时间: <br>
* 修改备注: <br>
* </p>
*/
public final class JSONUtils {
private final static ObjectMapper objectMapper = new ObjectMapper();
private JSONUtils() {
}
public static ObjectMapper getInstance() {
return objectMapper;
}
/**
* javaBean,list,array convert to json string
*/
public static String obj2json(Object obj)
throws JsonProcessingException {
return objectMapper.writeValueAsString(obj);
}
/**
* json string convert to javaBean
*/
public static <T> T json2pojo(String jsonStr, Class<T> clazz)
throws IOException {
return objectMapper.readValue(jsonStr, clazz);
}
/**
* json string convert to map
*/
public static <T> Map json2map(String jsonStr)
throws IOException {
return objectMapper.readValue(jsonStr, Map.class);
}
/**
* json string convert to map with javaBean
*/
public static <T> Map<String, T> json2map(String jsonStr, Class<T> clazz)
throws IOException {
Map<String, Map<String, Object>> map = objectMapper.readValue(jsonStr,
new TypeReference<Map<String, T>>() {
});
Map<String, T> result = new HashMap<>();
for (Map.Entry<String, Map<String, Object>> entry : map.entrySet()) {
result.put(entry.getKey(), map2pojo(entry.getValue(), clazz));
}
return result;
}
/**
* json array string convert to list with javaBean
*/
public static <T> List<T> json2list(String jsonArrayStr, Class<T> clazz)
throws IOException {
List<Map<String, Object>> list = objectMapper.readValue(jsonArrayStr,
new TypeReference<List<T>>() {
});
List<T> result = new ArrayList<T>();
for (Map<String, Object> map : list) {
result.add(map2pojo(map, clazz));
}
return result;
}
/**
* map convert to javaBean
*/
public static <T> T map2pojo(Map map, Class<T> clazz) {
return objectMapper.convertValue(map, clazz);
}
}
如果需要新增一种语言的话,只需新建一个class,实现 I18nPropertiesStrategy
接口即可。
5、使用
我们只需要把返回结果中的内容使用 properties
文件中的 key
替换即可,如有动态数据用 {}
填充即可。
感谢
作者:不敲代码的攻城狮
出处:https://www.cnblogs.com/leigq/
任何傻瓜都能写出计算机可以理解的代码。好的程序员能写出人能读懂的代码。