1. 国际化的目标
1). 如何配置国际化资源文件
I. Action 范围资源文件: 在Action类文件所在的路径建立名为 ActionName_language_country.properties 的文件
II. 包范围资源文件: 在包的根路径下建立文件名为 package_language_country.properties 的属性文件,
一旦建立,处于该包下的所有 Action 都可以访问该资源文件。注意:包范围资源文件的 baseName 就是package,不是Action所在的包名。
III. 全局资源文件
> 命名方式: basename_language_country.properties
> struts.xml <constant name="struts.custom.i18n.resources" value="baseName"/>
IV. 国际化资源文件加载的顺序如何呢 ? 离当前 Action 较近的将被优先加载.
假设我们在某个 ChildAction 中调用了getText("username"):
(1) 加载和 ChildAction 的类文件在同一个包下的系列资源文件 ChildAction.properties
(2) 加载 ChildAction 实现的接口 IChild,且和 IChildn 在同一个包下 IChild.properties 系列资源文件。
(3) 加载 ChildAction 父类 Parent,且和 Parent 在同一个包下的 baseName 为 Parent.properties 系列资源文件。
(4) 若 ChildAction 实现 ModelDriven 接口,则对于getModel()方法返回的model 对象,重新执行第(1)步操作。
(5) 查找当前包下 package.properties 系列资源文件。
(6) 沿着当前包上溯,直到最顶层包来查找 package.properties 的系列资源文件。
(7) 查找 struts.custom.i18n.resources 常量指定 baseName 的系列资源文件。
(8) 直接输出该key的字符串值。
2). 如何在页面上 和 Action 类中访问国际化资源文件的 value 值
I. 在 Action 类中. 若 Action 实现了 TextProvider 接口, 则可以调用其 getText() 方法获取 value 值
> 通过继承 ActionSupport 的方式。
II. 页面上可以使用 s:text 标签; 对于表单标签可以使用表单标签的 key 属性值
> 若有占位符, 则可以使用 s:text 标签的 s:param 子标签来填充占位符
> 可以利用标签和 OGNL 表达式直接访问值栈中的属性值(对象栈 和 Map 栈)
time=Time:{0}
<s:text name="time">
<s:param value="date"></s:param>
</s:text>
------------------------------------
time2=Time:${date}
<s:text name="time2"></s:text>
3). 实现通过超链接切换语言.
I. 关键之处在于知道 Struts2 框架是如何确定 Local 对象的 !
II. 可以通过阅读 I18N 拦截器知道.
III. 具体确定 Locale 对象的过程:
> Struts2 使用 i18n 拦截器 处理国际化,并且将其注册在默认的拦截器栈中
> i18n拦截器在执行Action方法前,自动查找请求中一个名为 request_locale 的参数。
如果该参数存在,拦截器就将其作为参数,转换成Locale对象,并将其设为用户默认的Locale(代表国家/语言环境)。
并把其设置为 session 的 WW_TRANS_I18N_LOCALE 属性
> 若 request 没有名为request_locale 的参数,则 i18n 拦截器会从 Session 中获取 WW_TRANS_I18N_LOCALE 的属性值,
若该值不为空,则将该属性值设置为浏览者的默认Locale
> 若 session 中的 WW_TRANS_I18N_LOCALE 的属性值为空,则从 ActionContext 中获取 Locale 对象。
IV. 具体实现: 只需要在超连接的后面附着 request_locale 的请求参数, 值是 语言国家 代码.
<a href="testI18n.action?request_locale=en_US">English</a>
<a href="testI18n.action?request_locale=zh_CN">中文</a>
> 注意: 超链接必须是一个 Struts2 的请求, 即使 i18n 拦截器工作!
> 注意: 超链接必须是一个 Struts2 的请求, 即使 i18n 拦截器工作!
使用label标签时要用%{getText{'username'}}方式也可以从国际化资源文件中获取value值 因为此时在对象栈中有
DefaultTypeProvider的一个实例 该对象中提供访问资源化文件的方法 同时还要通知label中放入的不再是普通的字符串
而是一个OGNL表达式 可以使用%{}包装起来
如果form的主题为simple的时候 所有设置都不起作用
这时使用s:text 标签 和强制ognl解析
有占位符的国际化资源文件的输出方式:
新建acton I18nAction
还可以使用time=Time:${date} 来直接获取值栈中的属性值
实现通过超链接切换语言:
关键之处在于struts2框架是如何确定Local对象的 i18n拦截器 I18nInterceptor
publicStringintercept(ActionInvocationinvocation)throwsException{
if
(
LOG
.
isDebugEnabled
())
{
LOG
.
debug
(
"intercept '#0/#1' {"
,
invocation
.
getProxy
().
getNamespace
(),
invocation
.
getProxy
().
getActionName
());
}
//get requested locale
Map
<
String
,
Object
>
params
=
invocation
.
getInvocationContext
().
getParameters
();
//保持session的falg默认为true
boolean
storeInSession
=
true
;
//parameteName 为request_locale
//requsetedLocale为转换过的浏览器参数
Object
requestedLocale
=
findLocaleParameter
(
params
,
parameterName
);
<!--
---
findLocaleParameter
private
Object
findLocaleParameter
(
Map
<
String
,
Object
>
params
,
String
parameterName
)
{
//将从浏览器带过来的request_locale参数保存到requestedLoclae中
Object
requestedLocale
=
params
.
remove
(
parameterName
);
//如果requestedLocale不为空 类型是数组 并取长度是1
if
(
requestedLocale
!=
null
&&
requestedLocale
.
getClass
().
isArray
()
&&
((
Object
[])
requestedLocale
).
length
==
1
)
{
//将数组中的参数取出赋给requestedLocale
requestedLocale
=
((
Object
[])
requestedLocale
)[
0
];
if
(
LOG
.
isDebugEnabled
())
{
LOG
.
debug
(
"requested_locale=#0"
,
requestedLocale
);
}
}
return
requestedLocale
;
}
--> //如果请求擦拭你为空就从requestOnlyParmeteName中去找
if
(
requestedLocale
==
null
)
{
//requestOnlyParameterName默认为request_only_locale
requestedLocale
=
findLocaleParameter
(
params
,
requestOnlyParameterName
);
//如果上面的保存到request中的不为空 则将保存到session的flag设置为false
if
(
requestedLocale
!=
null
)
{
storeInSession
=
false
;
}
}
//将找到的requestLocale参数赋值给local
Locale
locale
=
getLocaleFromParam
(
requestedLocale
);
<!--
---
getLocaleFromParam
protected
Locale
getLocaleFromParam
(
Object
requestedLocale
)
{
Locale
locale
=
null
;
//如果传进来的参数不为空
if
(
requestedLocale
!=
null
)
{
//如果参数的类型为Locale 就强转为Locale 如果不是就执行下面的代码 将将参数转换为Locale类型并赋值
locale
=
(
requestedLocale
instanceof
Locale
)
?
(
Locale
)
requestedLocale
:
LocalizedTextUtil
.
localeFromString
(
requestedLocale
.
toString
(),
null
);
if
(
locale
!=
null
&&
LOG
.
isDebugEnabled
())
{
LOG
.
debug
(
"applied request locale=#0"
,
locale
);
}
}
return
locale
;
}
-->
//获取sessionMap
Map
<
String
,
Object
>
session
=
invocation
.
getInvocationContext
().
getSession
();
//如果session不为空 并取locale不为空 就把参数保存到session中
if
(
session
!=
null
)
{
synchronized
(
session
)
{
if
(
locale
==
null
)
{
storeInSession
=
false
;
locale
=
readStoredLocale
(
invocation
,
session
);
}
if
(
storeInSession
)
{
session
.
put
(
attributeName
,
locale
);
}
}
}
saveLocale
(
invocation
,
locale
);
<!--
//将获取到的locale值放入context 中 在值栈的栈顶可见
protected
void
saveLocale
(
ActionInvocation
invocation
,
Locale
locale
)
{
invocation
.
getInvocationContext
().
setLocale
(
locale
);
}
-->
if
(
LOG
.
isDebugEnabled
())
{
LOG
.
debug
(
"before Locale=#0"
,
invocation
.
getStack
().
findValue
(
"locale"
));
}
final
String
result
=
invocation
.
invoke
();
if
(
LOG
.
isDebugEnabled
())
{
LOG
.
debug
(
"after Locale=#0"
,
invocation
.
getStack
().
findValue
(
"locale"
));
LOG
.
debug
(
"intercept } "
);
}
return
result
;
}
//默认请况下走的是左边这条线 如果通过url改变locle的值则走右边 设置之后 如果没有设置的url访问就会从session取对应的locale参数去使用