使用框架:HAP 3.5.6-RELEASE
数据库:mySQL 5.7
说明:这篇文章是为HAP框架编写的。目的是为了分享这个框架的国际化方案,可能还有更好的方法,这里提供一下我的方案:使用框架的 描述维护 功能实现代码内部的国际化(也就是多语言)。
文章目录
1. 使用MessageSource,配置Bean
Spring Boot、SpringMVC进行i18n国际化支持:使用 MessageSource 。HAP属于SpringMVC,使用的也是MessageSource,但是使用的bean是重新定义的,这个bean的数据源是HAP的基础模块 描述维护
这里需要配置默认的bean:
<bean id="messageSource" class="com.hand.hap.core.i18n.CacheMessageSource"/>
第一步就完成了。
2. 观察nls()方法
1. 观察 BaseController这个类
在HAP中几乎所有的 Controller 都继承了这个方法,我们发现其中有两个 nls() 方法:
protected String nls(HttpServletRequest request, String code, Object[] args) {
Locale locale = RequestContextUtils.getLocale(request);
return this.messageSource.getMessage(code, args, code, locale);
}
protected String nls(HttpServletRequest request, String code) {
Locale locale = RequestContextUtils.getLocale(request);
return this.messageSource.getMessage(code, (Object[])null, code, locale);
}
这两个方法可以用来实现 code 和译文的转换;他们都使用了 String getMessage(String code, Object[] args, String defaultMessage, Locale locale) ;四个参数分别是 code ,要填充的参数,默认描述,和当前语种,这里就不细说了,之所以提到是为了改写这个方法,使得其在 service层 也可以使用,但是这里有一个问题,我们需要传递一个 HttpServletRequest 参数,这样会使得我们的函数使用起来非常不方便。
2. 获取HttpServletRequest
这里提供一种方法:
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
这种方式当Bean(这里是当service接口注入时)初始化时,Spring并没有注入一个request对象,而是注入了一个代理(proxy);当Bean中需要使用request对象时,通过该代理获取request对象。这种方式是线程安全的,为了方便使用我们这个方法,我们可以把它放在基类里,这个基类就是BaseServiceImpl。
3. 创建nls()方法
我们要创建的 nls() 方法是使用接口的方式,其主要目的是为了在其他功能上取巧,这里不多解释,你也可以像 BaseController 一样设置成 protected 方法,这里用接口的方式;
1. 创建接口
String nls(String code, Object[] args);
String nls(String code);
2. 注入 MessageSource
@Autowired
private MessageSource messageSource;
3. 创建nls()
@Override
public String nls(String code, Object[] args) {
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
Locale locale = RequestContextUtils.getLocale(request);
return this.messageSource.getMessage(code, args, code, locale);
}
@Override
public String nls(String code) {
return this.nls(code, null);
}
4. 测试方法
事先我们定义好一个code:student.studentnumber
我们的测试类在service里面
@Override
public void langTest() {
System.out.println(nls("student.studentnumber"));
}
我们在查询页面时调用这个方法:
@RequestMapping(value = "/student/query")
@ResponseBody
public ResponseData query(Student dto, @RequestParam(defaultValue = DEFAULT_PAGE) int page,
@RequestParam(defaultValue = DEFAULT_PAGE_SIZE) int pageSize, HttpServletRequest request)
{
IRequest requestContext = createRequestContext(request);
service.langTest();
return new ResponseData(service.select(requestContext, dto, page, pageSize));
}
中文结果
学号
英文结果
Student Number
这些说明我们可以通过这样的方式实现后台的国际化,这里还差的就是异常的国际化和统一管理。
4. 异常的国际化和错误提示的统一管理
1. 自定义异常
这里一定要使用自定义的异常,并且要实现的是IBaseException接口,当然也可以直接使用这两个异常:BaseException 和 BaseRuntimeExcepiton,为什么要实现这个接口,我们要看 BaseController 里面是如何处理的;
@ExceptionHandler({Exception.class})
public Object exceptionHandler(Exception exception, HttpServletRequest request) {
this.logger.error(exception.getMessage(), exception);
Throwable thr = this.getRootCause(exception);
IBaseException be;
Locale locale;
String messageKey;
String message;
if (!RequestUtil.isAjaxRequest(request) && !RequestUtil.isAPIRequest(request) && !ServletFileUpload.isMultipartContent(request)) {
ModelAndView view = new ModelAndView("500");
if (thr instanceof IBaseException) {
be = (IBaseException)thr;
locale = RequestContextUtils.getLocale(request);
messageKey = be.getDescriptionKey();
message = this.messageSource.getMessage(messageKey, be.getParameters(), messageKey, locale);
view.addObject("message", message);
}
return view;
} else {
ResponseData res = new ResponseData(false);
if (thr instanceof IBaseException) {
be = (IBaseException)thr;
locale = RequestContextUtils.getLocale(request);
messageKey = be.getDescriptionKey();
message = this.messageSource.getMessage(messageKey, be.getParameters(), messageKey, locale);
res.setCode(be.getCode());
res.setMessage(message);
} else {
Locale locale1 = new Locale("zh", "CN");
ResourceBundle resourceBundle = ResourceBundle.getBundle("message",locale1);
res.setMessage(thr.toString());
}
return res;
}
}
在这个方法里,我们可以看到,当它捕获到异常是会判断是否实现了IBaseException ,如果实现了,它会将messageKey 转换成译文;
我们这里可以自定义一个异常类:
public class DuplicateException extends RuntimeException implements IBaseException {
private String code;
private String descriptionKey;
private Object[] parameters;
public DuplicateException() {
}
public DuplicateException(String descriptionKey) {
super(descriptionKey);
this.code = null;
this.descriptionKey = descriptionKey;
this.parameters = null;
}
public DuplicateException(String descriptionKey, Object[] parameters) {
super(descriptionKey);
this.code = null;
this.descriptionKey = descriptionKey;
this.parameters = parameters;
}
public DuplicateException(String code, String descriptionKey, Object[] parameters) {
super(descriptionKey);
this.code = code;
this.descriptionKey = descriptionKey;
this.parameters = parameters;
}
@Override
public String getCode() {
return this.code;
}
@Override
public String getDescriptionKey() {
return this.descriptionKey;
}
@Override
public Object[] getParameters() {
return this.parameters;
}
@Override
public void setCode(String code) {
this.code = code;
}
@Override
public void setDescriptionKey(String descriptionKey) {
this.descriptionKey = descriptionKey;
}
@Override
public void setParameters(Object[] parameters) {
this.parameters = parameters;
}
}
2. 测试方法修改
@Override
public void langTest() {
throw new DuplicateException("student.studentnumber");
}
中文结果:
英文结果:
这样就实现了;
3.带参数的定义方式
如果想带参数的,可以这样做;
定义描述:
改写测试方法:
@Override
public void langTest() {
Object[] objects = {"我是参数1", "我是参数2"};
throw new DuplicateException("test.lang", objects);
}
运行结果:
这样就实现了带参数的实现方法了。
到这里就分享完了我关于HAP的国际化方案了,希望对你有帮助,如果有更好的方案也希望能在评论区分享给我谢谢。