2021SC@SDUSC
i18n:
i18n为internationalization的缩写,为国际化的实现。该包中有两个接口文件,分别为Bundle和BundleFactory,另外有一个I18nUtils类提供i18n格式化和解析路由的实用程序类。
I18nUtils:
在I18nUtils类中,提供的方法一共可以看作4个,逐个分析。
首先是findLocate方法,需要提供的参数有objectModel, attribute, parameters, defaultLocale, useLocate, useLocales, useBlankLocale以及test,并且对该方法进行了重载,重载后的方法减少了三个参数,转而使用默认值,默认useLocales和useBlankLocale为false,test为空。
public static Locale findLocale(Map objectModel,
String attribute,
Parameters parameters,
Locale defaultLocale,
boolean useLocale,
boolean useLocales,
boolean useBlankLocale,
LocaleValidator test) {
String localeStr;
Locale locale;
Request request = ObjectModelHelper.getRequest(objectModel);
localeStr = request.getParameter(attribute);
if (localeStr != null) {
locale = parseLocale(localeStr);
if (test == null || test.test("request", locale)) {
return locale;
}
}
HttpSession session = request.getSession(false);
if (session != null &&
((localeStr = (String) session.getAttribute(attribute)) != null)) {
locale = parseLocale(localeStr);
if (test == null || test.test("session", locale)) {
return locale;
}
}
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
if (cookie.getName().equals(attribute)) {
localeStr = cookie.getValue();
locale = parseLocale(localeStr);
if (test == null || test.test("cookie", locale)) {
return locale;
}
break;
}
}
}
if (parameters != null) {
localeStr = parameters.getParameter("locale", null);
if (localeStr != null) {
locale = parseLocale(localeStr);
if (test == null || test.test("sitemap", locale)) {
return locale;
}
}
}
if (useLocale && !useLocales) {
locale = request.getLocale();
if (test == null || test.test("request", locale)) {
return locale;
}
}
if (useLocales) {
Enumeration locales = request.getLocales();
while (locales.hasMoreElements()) {
locale = (Locale)locales.nextElement();
if (test == null || test.test("request", locale)) {
return locale;
}
}
}
if (defaultLocale != null) {
locale = defaultLocale;
if (test == null || test.test("default", locale)) {
return locale;
}
}
if (useBlankLocale) {
locale = new Locale("", "");
if (test == null || test.test("blank", locale)) {
return locale;
}
}
return null;
}
findLocale方法从objectModel中寻找合适的语言环境,在该方法中,分别对request, session, cookie, sitemap,本地设置浏览器和服务器默认值等返回。
parseLocale同样进行了重载,都需要给出语言环境字符,如果给定语言环境则若字符串为空返回给定的语言环境,若不给定,则会返回VM默认环境。
storeLocale方法在请求,会话或cookie中存储Locale,在参数中通过布尔值设置存储在什么位置,根据设置的参数将给定的Locale存储。
该类中提供的最后一个方法为matchesI18nNamespace,通过该方法对i18n的命名空间进行匹配。
XMLResourceBundle:
该方法实现了Bundle接口,用来表示单个的XML消息包,实现了如下的XML格式:
<catalogue xml:lang="en">
<message key="key1">Message <br/> Value 1</message>
<message key="key2">Message <br/> Value 1</message>
...
</catalogue>
通过key来指示值,返回的对象为ParamSaxBuffer的实例,如果此包中不存在键的值,则查询父包。
该类提供了reload, getLocale, getSourceURI, getValidity, getObject,getString 方法,其中的get方法都容易理解,都是根据需要返回所需要的值。
reload方法根据 source URI 重新加载XML包,
protected boolean reload(SourceResolver resolver, long interval) {
Source newSource = null;
Map newValues;
try {
int valid = this.validity == null ? SourceValidity.INVALID : this.validity.isValid();
if (valid != SourceValidity.VALID) {
newSource = resolver.resolveURI(this.sourceURI);
SourceValidity newValidity = newSource.getValidity();
if (valid == SourceValidity.INVALID || this.validity.isValid(newValidity) != SourceValidity.VALID) {
newValues = new HashMap();
SourceUtil.toSAX(newSource, new SAXContentHandler(newValues));
synchronized (this) {
if (interval > 0 && newValidity != null) {
this.validity = new DelayedValidity(interval, newValidity);
} else {
this.validity = newValidity;
}
this.values = newValues;
}
}
}
return true;
} catch (MalformedURLException e) {
getLogger().error("Bundle <" + this.sourceURI + "> not loaded: Invalid URI", e);
newValues = Collections.EMPTY_MAP;
} catch (ResourceNotFoundException e) {
if (getLogger().isDebugEnabled()) {
getLogger().info("Bundle <" + sourceURI + "> not loaded: Source URI not found", e);
} else if (getLogger().isInfoEnabled()) {
getLogger().info("Bundle <" + sourceURI + "> not loaded: Source URI not found");
}
newValues = Collections.EMPTY_MAP;
} catch (SourceNotFoundException e) {
if (getLogger().isDebugEnabled()) {
getLogger().debug("Bundle <" + sourceURI + "> not loaded: Source URI not found");
}
newValues = Collections.EMPTY_MAP;
} catch (CascadingIOException e) {
if (getLogger().isDebugEnabled()) {
getLogger().debug("Bundle <" + sourceURI + "> not loaded: Source URI not found");
}
newValues = Collections.EMPTY_MAP;
} catch (SAXException e) {
getLogger().error("Bundle <" + sourceURI + "> not loaded: Invalid XML", e);
newValues = this.values;
} catch (Exception e) {
getLogger().error("Bundle <" + sourceURI + "> not loaded: Exception", e);
newValues = this.values;
} finally {
if (newSource != null) {
resolver.release(newSource);
}
}
synchronized (this) {
if (interval > 0) {
this.validity = new ExpiresValidity(interval);
} else {
this.validity = null;
}
this.values = newValues;
}
return false;
}
若保存的validity不是有效的则获取新的source和validity,同时施加了同步锁,确保代码就会以原子的方式执行,当多个线程在执行这段代码的时候,它们是互斥的,不会相互干扰,不会同时执行。
后续代码则是对各种异常的捕获。
该类中,还包含一个私有类,SAXContentHandler控制XML bundle和创建映射。
XMLResourceBundleFactory:
该类实现了BundleFactory接口,同时实现了设置,回收,接口可维护以及线程安全。
该类提供了多个get方法,可以获取目录,父级语言环境,缓存键, sourceURI,同时提供了更新缓存方法。
该类对select方法进行了重载,可以通过多种不同参数调用,而最终,select方法调用自身的私有方法_select返回bundle,首先从缓存中查找,若未找到,则会从父bundle中寻找,寻找时会进行对自身的递归调用,逐层遍历,若遍历完本层,则会寻找父Locale,最后将找到的结果返回。
_create方法同样作为私有方法,是供_select使用,创建bundle。
在获取缓存键方法中,同样使用了index记录当前的位置,通过递归来获取。