2019独角兽企业重金招聘Python工程师标准>>>
目前整体的架构是,前端展示采用的nodejs,后台服务采用java实现,听过rest服务接口,结构如下:
语言国际化面临的问题:
1、在浏览器端,有一些js控件,比如时间选择器等,需要在浏览器端进行语言国际化的实现
2、展示层采用nodejs实现,主要的业务逻辑为页面的拼装,这部分是语言国际化实现的最重要的一部分
3、在服务层,有可能会由于业务逻辑异常等原因,会有错误提示信息抛出到展示层,进而在页面上进行显示,这部分内容也需要进行国际化
4、数据库层有些初始化的数据,该部分数据暂时没有考虑国际化
5、面对以上的问题,貌似要在每个节点都要维护国际化资源信息,但这样势必给我们带来维护成本,我们必须要将所有的国际化资源配置集中到一个节点上
我们最终选择将国际化资源文件放在nodejs端,nodejs采用了express框架,可以将资源文件放在public目录下,这样该文件可以在服务端和客户端都能够进行访问,这样就能够同时满足1和2,浏览器端和nodejs的服务端可以同时访问该文件;服务层采用java完成的,提供的是rest服务,为了实现这些服务能够横向扩展,这些服务都是无状态的,换句话说,这些服务不会因为客户端浏览器的语言环境或用户要求使用的语言环境,而去保存一个用户的状态,但是国际化如何去实现呢?我们的解决方案是,所有的服务层返回的异常信息(正常的业务逻辑都是显示数据,一般情况下只有发生异常了,给用户提示时,才会根据不同的语言环境,提示不同的语言信息)用一个关键字来代替,到了展示层在根据客户浏览器展示不同的语言信息,这样就能够将资源配置文件提到nodejs端,同时保留了服务无状态的特性,至此所有的语言国际化配置信息都集中到了nodejs端
接下来我们如何在nodejs端进行实现呢?我们在第一次接收到用户请求时,会根据请求头信息,获取浏览器的语言设置,并将此信息保存到cookie中,同时服务端根据该值判断需要读取哪个资源文件中的信息,借助nodejs端的i18n组件可以实现,客户端切换语言,只要更改客户端的cookie值就可以了,至此语言国际化可以实现了
接下来我们说说时间国际化,时间国际化的意思是什么呢?,给大家举个例子,我们在页面表单创建了一条数据,我们是在北京操作的,该条数据的创建时间是2017-11-02 14:43:00 如果该时间在页面展示时,在北京查看需要显示2017-11-02 14:43:00,如果在美国华盛顿,看到的应该是2017-11-02 2:43:00,因为美国华盛顿比北京晚12个小时;如果实现这个功能,应该从两方面来说,第一、我们如何存储,怎么将时间保存到数据库中,其实可以有以下几种解决方法,
- 数据库中保存一个long类型的数字,保存的就是从1970年1月1日到目前为止的毫秒数,这个通过java的System.currentTimeMillis()直接获取到,这种方式最简单,不用进行转换,因为全世界任何地方从1970年1月1日到目前为止的毫秒数都是一致到,但这样去数据库中查看该条数据时,会比较费劲,不知道这个时间到底是几月几日,还需要借助工具才能查看到,如果大家觉得这不是问题,可以采用这种方式
- 数据库中,同时保存时间和时区,这种方式如果用MySQL进行保存,需要有两个字段
- 将时间格式转换成UTC时间,这种方式也相对比较简单,时间格式也很友好,目前我们采用了这种方式进行存储到
我们持久层框架采用了MyBaties,自己是实现了DateTypeHandler,这样在不影响原有代码的情况下,实现了数据库中UTC时间格式的保存与查询,代码实现如下:
package XXXXXXXXXXXXX;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Date;
import java.util.TimeZone;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
/**
* 处理时间类型,将时间转换成UTC时间,然后保存到数据库中
*
* @author sunhuaili
*
*/
public class DateTypeHandler extends BaseTypeHandler
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType)
throws SQLException {
TimeZone timeZone = TimeZone.getDefault();
long utcTime = parameter.getTime() - timeZone.getRawOffset();
Timestamp timestamp = new Timestamp(utcTime);
ps.setTimestamp(i, timestamp);
}
@Override
public Date getNullableResult(ResultSet rs, String columnName) throws SQLException {
Timestamp sqlTimestamp = rs.getTimestamp(columnName);
if (sqlTimestamp != null) {
TimeZone timeZone = TimeZone.getDefault();
long utcTime = sqlTimestamp.getTime();
return new Date(utcTime + timeZone.getRawOffset());
}
return null;
}
@Override
public Date getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
Timestamp sqlTimestamp = rs.getTimestamp(columnIndex);
if (sqlTimestamp != null) {
TimeZone timeZone = TimeZone.getDefault();
long utcTime = sqlTimestamp.getTime();
return new Date(utcTime + timeZone.getRawOffset());
}
return null;
}
@Override
public Date getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
Timestamp sqlTimestamp = cs.getTimestamp(columnIndex);
if (sqlTimestamp != null) {
TimeZone timeZone = TimeZone.getDefault();
long utcTime = sqlTimestamp.getTime();
return new Date(utcTime + timeZone.getRawOffset());
}
return null;
}
}
第二就是要考虑展示到问题,这个比较有工作量到地方,难度不大,但每个显示时间的地方都要转换成当地时间,该转换其实是有两种地方,1. nodejs服务器端,2. 前端js通过ajax访问时,因为用了nodejs,所以该逻辑是可以复用的