在src下新建资源文件,格式为baseName_language_country.properties。因为我用的是maven搭建的项目,所以这个资源文件放在了src/main/resources/下。
baseName是资源文件的基本名称,可自由定义。language和country不可随意变化,必须是java支持的语言和国家。
@Test public void test(){ Locale[] locals=Locale.getAvailableLocales(); for (Locale locale : locals) { System. out.print("language: "+locale.getLanguage()+ " "); System. out.print("country: "+locale.getCountry()); System. out.println(); } }
资源文件一共有三种格式
baseName_language_country.properties
baseName_language.properties
baseName_language.properties
baseName.properties
不过第一种最常用。
接下来编写我们的资源文件:
resource_zh_CN.properties
index.title= u767Bu5F55u9875u9762
index.username= u7528u6237u540D
index.password= u5BC6u7801
index.submit= u63D0u4EA4
看到的内容是自动编译成Unicode编码,也可用native2ascii命令编码后在粘贴过来。
resource_en_US.properties:
index.title= Login Page
index.username= UserName
index.password= Password
index.submit= Submit
接下来要在struts.xml中加载国际化资源文件
<constant name ="struts.custom.i18n.resources" value ="resource" ></ constant> <constant name= "struts.i18n.encoding" value ="utf-8" />
显示一下我们的页面代码
<%@ page language= "java" contentType = "text/html; charset=UTF-8" pageEncoding ="UTF-8" %> <%@ taglib prefix ="s" uri= "/struts-tags"%> <! DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd" > < html> < head> < meta http-equiv ="Content-Type" content ="text/html; charset=UTF-8" > < title>< s:text name ="index.title" ></ s:text></ title > </ head> < body> <a href ="lang.action?request_locale=zh_CN" > 中文</ a > <a href ="lang.action?request_locale=en_US" > English</ a > <s:form id= "loginform" method ="post" > < s:textfield name ="username" key ="index.username" ></ s:textfield> < s:textfield name ="password" key ="index.password" ></ s:textfield> < s:submit key ="index.submit" ></ s:submit> </s:form > </ body> </ html>
package com.zhao.action; import com.opensymphony.xwork2.ActionSupport; public class LangAction extends ActionSupport { @Override public String execute() throws Exception { // TODO Auto-generated method stub return SUCCESS ; } }
由此看见,我们的LangAction是直接返回这个页面的,那是如何完成中英文切换的过程的呢?
实际上我们使用了默认的拦截器
url中的这个参数 request_locale会被 i18n 拦截器读取,然后根据这个值设置语言环境。 i18n 拦截器是struts中default里面自带的拦截器。为此我觉得我需要去struts-default.xml中看一看:
<? xml version ="1.0" encoding= "UTF-8" ?> <!-- /* * $Id: struts-default.xml 1546514 2013-11-29 07:12:14Z lukaszlenart $ * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ --> <!-- When declaring beans in this file you must either use name="struts" or don't name the bean at all. The name="struts" must be used when alias was defined in {@link org.apache.struts2.config.DefaultBeanSelectionProvider} - it is then the default bean's name and {@link org.apache.struts2.config.DefaultBeanSelectionProvider} links name "struts" with "default" (aliasing it) If name won't be defined then the "default" value will be used {@link com.opensymphony.xwork2.inject.Container#DEFAULT_NAME} and {@link com.opensymphony.xwork2.inject.Inject} --> <! DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd" > < struts> <bean class ="com.opensymphony.xwork2.ObjectFactory" name ="struts" /> <bean type ="com.opensymphony.xwork2.factory.ResultFactory" name= "struts" class ="org.apache.struts2.factory.StrutsResultFactory" /> <bean type ="com.opensymphony.xwork2.factory.ActionFactory" name= "struts" class ="com.opensymphony.xwork2.factory.DefaultActionFactory" /> <bean type ="com.opensymphony.xwork2.factory.ConverterFactory" name= "struts" class ="com.opensymphony.xwork2.factory.DefaultConverterFactory" /> <bean type ="com.opensymphony.xwork2.factory.InterceptorFactory" name= "struts" class ="com.opensymphony.xwork2.factory.DefaultInterceptorFactory" /> <bean type ="com.opensymphony.xwork2.factory.ValidatorFactory" name= "struts" class ="com.opensymphony.xwork2.factory.DefaultValidatorFactory" /> <bean type ="com.opensymphony.xwork2.FileManager" class ="com.opensymphony.xwork2.util.fs.DefaultFileManager" name= "system" scope ="singleton" /> <bean type ="com.opensymphony.xwork2.FileManagerFactory" class ="com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory" name= "struts" scope ="singleton" /> <bean type ="com.opensymphony.xwork2.ActionProxyFactory" name= "struts" class ="org.apache.struts2.impl.StrutsActionProxyFactory" /> <bean type ="com.opensymphony.xwork2.ActionProxyFactory" name= "prefix" class ="org.apache.struts2.impl.PrefixBasedActionProxyFactory" /> <bean type ="com.opensymphony.xwork2.conversion.ObjectTypeDeterminer" name= "struts" class ="com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer" /> <bean type ="com.opensymphony.xwork2.util.PatternMatcher" name= "struts" class ="com.opensymphony.xwork2.util.WildcardHelper" /> <bean type ="com.opensymphony.xwork2.util.PatternMatcher" name= "namedVariable" class ="com.opensymphony.xwork2.util.NamedVariablePatternMatcher" /> <bean type ="com.opensymphony.xwork2.util.PatternMatcher" name= "regex" class ="org.apache.struts2.util.RegexPatternMatcher" /> <bean type ="org.apache.struts2.dispatcher.mapper.ActionMapper" name= "struts" class ="org.apache.struts2.dispatcher.mapper.DefaultActionMapper" /> <bean type ="org.apache.struts2.dispatcher.mapper.ActionMapper" name= "composite" class ="org.apache.struts2.dispatcher.mapper.CompositeActionMapper" /> <bean type ="org.apache.struts2.dispatcher.mapper.ActionMapper" name= "restful" class ="org.apache.struts2.dispatcher.mapper.RestfulActionMapper" /> <bean type ="org.apache.struts2.dispatcher.mapper.ActionMapper" name= "restful2" class ="org.apache.struts2.dispatcher.mapper.Restful2ActionMapper" /> <bean type ="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name= "jakarta" class ="org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest" scope= "default"/> <bean type ="org.apache.struts2.views.TagLibraryDirectiveProvider" name= "s" class ="org.apache.struts2.views.DefaultTagLibrary" /> <bean type ="org.apache.struts2.views.TagLibraryModelProvider" name= "s" class ="org.apache.struts2.views.DefaultTagLibrary" /> <bean class ="org.apache.struts2.views.freemarker.FreemarkerThemeTemplateLoader" /> <bean class ="org.apache.struts2.views.freemarker.FreemarkerManager" name= "struts" /> <bean class ="org.apache.struts2.views.velocity.VelocityManager" name= "struts" optional ="true" /> <bean class ="org.apache.struts2.components.template.TemplateEngineManager" /> <bean type ="org.apache.struts2.components.template.TemplateEngine" name= "ftl" class ="org.apache.struts2.components.template.FreemarkerTemplateEngine" /> <bean type ="org.apache.struts2.components.template.TemplateEngine" name= "vm" class ="org.apache.struts2.components.template.VelocityTemplateEngine" /> <bean type ="org.apache.struts2.components.template.TemplateEngine" name= "jsp" class ="org.apache.struts2.components.template.JspTemplateEngine" /> <bean type ="com.opensymphony.xwork2.conversion.impl.XWorkConverter" name= "struts" class ="com.opensymphony.xwork2.conversion.impl.XWorkConverter" /> <bean type ="com.opensymphony.xwork2.conversion.ConversionPropertiesProcessor" name= "struts" class ="com.opensymphony.xwork2.conversion.impl.DefaultConversionPropertiesProcessor" /> <bean type ="com.opensymphony.xwork2.conversion.ConversionFileProcessor" name= "struts" class ="com.opensymphony.xwork2.conversion.impl.DefaultConversionFileProcessor" /> <bean type ="com.opensymphony.xwork2.conversion.ConversionAnnotationProcessor" name= "struts" class ="com.opensymphony.xwork2.conversion.impl.DefaultConversionAnnotationProcessor" /> <bean type ="com.opensymphony.xwork2.conversion.TypeConverterCreator" name= "struts" class ="com.opensymphony.xwork2.conversion.impl.DefaultTypeConverterCreator" /> <bean type ="com.opensymphony.xwork2.conversion.TypeConverterHolder" name= "struts" class ="com.opensymphony.xwork2.conversion.impl.DefaultTypeConverterHolder" /> <bean class ="com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter" /> <bean type ="com.opensymphony.xwork2.conversion.impl.CollectionConverter" name= "struts" class ="com.opensymphony.xwork2.conversion.impl.CollectionConverter" scope= "singleton"/> <bean type ="com.opensymphony.xwork2.conversion.impl.ArrayConverter" name= "struts" class ="com.opensymphony.xwork2.conversion.impl.ArrayConverter" scope= "singleton"/> <bean type ="com.opensymphony.xwork2.conversion.impl.DateConverter" name= "struts" class ="com.opensymphony.xwork2.conversion.impl.DateConverter" scope= "singleton"/> <bean type ="com.opensymphony.xwork2.conversion.impl.NumberConverter" name= "struts" class ="com.opensymphony.xwork2.conversion.impl.NumberConverter" scope= "singleton"/> <bean type ="com.opensymphony.xwork2.conversion.impl.StringConverter" name= "struts" class ="com.opensymphony.xwork2.conversion.impl.StringConverter" scope= "singleton"/> <bean type ="com.opensymphony.xwork2.TextProvider" name ="struts" class ="com.opensymphony.xwork2.TextProviderSupport" scope= "default" /> <bean type ="com.opensymphony.xwork2.LocaleProvider" name ="struts" class ="com.opensymphony.xwork2.DefaultLocaleProvider" scope= "singleton" /> <bean type ="org.apache.struts2.components.UrlRenderer" name ="struts" class ="org.apache.struts2.components.ServletUrlRenderer" /> <bean type ="org.apache.struts2.views.util.UrlHelper" name ="struts" class ="org.apache.struts2.views.util.DefaultUrlHelper" /> <bean type ="com.opensymphony.xwork2.util.ValueStackFactory" name= "struts" class ="com.opensymphony.xwork2.ognl.OgnlValueStackFactory" /> <bean type ="com.opensymphony.xwork2.util.reflection.ReflectionProvider" name= "struts" class ="com.opensymphony.xwork2.ognl.OgnlReflectionProvider" /> <bean type ="com.opensymphony.xwork2.util.reflection.ReflectionContextFactory" name= "struts" class ="com.opensymphony.xwork2.ognl.OgnlReflectionContextFactory" /> <bean type ="com.opensymphony.xwork2.TextProvider" name ="system" class ="com.opensymphony.xwork2.DefaultTextProvider" /> <bean type ="com.opensymphony.xwork2.conversion.NullHandler" name= "java.lang.Object" class ="com.opensymphony.xwork2.conversion.impl.InstantiatingNullHandler" /> <bean type ="com.opensymphony.xwork2.validator.ActionValidatorManager" name= "struts" class ="com.opensymphony.xwork2.validator.AnnotationActionValidatorManager" /> <bean type ="com.opensymphony.xwork2.validator.ActionValidatorManager" name= "no-annotations" class ="com.opensymphony.xwork2.validator.DefaultActionValidatorManager" /> <bean type ="com.opensymphony.xwork2.validator.ValidatorFactory" class ="com.opensymphony.xwork2.validator.DefaultValidatorFactory" /> <bean type ="com.opensymphony.xwork2.validator.ValidatorFileParser" class ="com.opensymphony.xwork2.validator.DefaultValidatorFileParser" /> <bean class ="com.opensymphony.xwork2.ognl.OgnlUtil" /> <bean type ="com.opensymphony.xwork2.util.TextParser" name ="struts" class ="com.opensymphony.xwork2.util.OgnlTextParser" scope= "singleton"/> <bean type= "ognl.PropertyAccessor" name ="com.opensymphony.xwork2.util.CompoundRoot" class ="com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor" /> <bean type= "ognl.PropertyAccessor" name ="java.lang.Object" class ="com.opensymphony.xwork2.ognl.accessor.ObjectAccessor" /> <bean type= "ognl.PropertyAccessor" name ="java.util.Iterator" class ="com.opensymphony.xwork2.ognl.accessor.XWorkIteratorPropertyAccessor" /> <bean type= "ognl.PropertyAccessor" name ="java.util.Enumeration" class ="com.opensymphony.xwork2.ognl.accessor.XWorkEnumerationAccessor" /> <bean type= "ognl.PropertyAccessor" name ="java.util.List" class ="com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor" /> <bean type= "ognl.PropertyAccessor" name ="java.util.Set" class ="com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor" /> <bean type= "ognl.PropertyAccessor" name ="java.util.Map" class ="com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor" /> <bean type= "ognl.PropertyAccessor" name ="java.util.Collection" class ="com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor" /> <bean type= "ognl.PropertyAccessor" name ="com.opensymphony.xwork2.ognl.ObjectProxy" class ="com.opensymphony.xwork2.ognl.accessor.ObjectProxyPropertyAccessor" /> <bean type= "ognl.MethodAccessor" name ="java.lang.Object" class ="com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor" /> <bean type= "ognl.MethodAccessor" name ="com.opensymphony.xwork2.util.CompoundRoot" class ="com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor" /> <bean class ="org.apache.struts2.views.jsp.ui.OgnlTool" /> <bean type ="org.apache.struts2.dispatcher.StaticContentLoader" class ="org.apache.struts2.dispatcher.DefaultStaticContentLoader" name= "struts" /> <bean type ="com.opensymphony.xwork2.UnknownHandlerManager" class ="com.opensymphony.xwork2.DefaultUnknownHandlerManager" name= "struts" /> <!-- Silly workarounds for OGNL since there is currently no way to flush its internal caches --> <bean type= "ognl.PropertyAccessor" name ="java.util.ArrayList" class ="com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor" /> <bean type= "ognl.PropertyAccessor" name ="java.util.HashSet" class ="com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor" /> <bean type= "ognl.PropertyAccessor" name ="java.util.HashMap" class ="com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor" /> <package name= "struts-default" abstract ="true" > < result-types> < result-type name ="chain" class ="com.opensymphony.xwork2.ActionChainResult" /> < result-type name ="dispatcher" class ="org.apache.struts2.dispatcher.ServletDispatcherResult" default= "true"/> < result-type name ="freemarker" class ="org.apache.struts2.views.freemarker.FreemarkerResult" /> < result-type name ="httpheader" class ="org.apache.struts2.dispatcher.HttpHeaderResult" /> < result-type name ="redirect" class ="org.apache.struts2.dispatcher.ServletRedirectResult" /> < result-type name ="redirectAction" class ="org.apache.struts2.dispatcher.ServletActionRedirectResult" /> < result-type name ="stream" class ="org.apache.struts2.dispatcher.StreamResult" /> < result-type name ="velocity" class ="org.apache.struts2.dispatcher.VelocityResult" /> < result-type name ="xslt" class ="org.apache.struts2.views.xslt.XSLTResult" /> < result-type name ="plainText" class ="org.apache.struts2.dispatcher.PlainTextResult" /> < result-type name ="postback" class ="org.apache.struts2.dispatcher.PostbackResult" /> </ result-types> < interceptors> < interceptor name ="alias" class ="com.opensymphony.xwork2.interceptor.AliasInterceptor" /> < interceptor name ="autowiring" class ="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor" /> < interceptor name ="chain" class ="com.opensymphony.xwork2.interceptor.ChainingInterceptor" /> < interceptor name ="conversionError" class ="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor" /> < interceptor name ="cookie" class ="org.apache.struts2.interceptor.CookieInterceptor" /> < interceptor name ="cookieProvider" class ="org.apache.struts2.interceptor.CookieProviderInterceptor" /> < interceptor name ="clearSession" class ="org.apache.struts2.interceptor.ClearSessionInterceptor" /> < interceptor name ="createSession" class ="org.apache.struts2.interceptor.CreateSessionInterceptor" /> < interceptor name ="debugging" class ="org.apache.struts2.interceptor.debugging.DebuggingInterceptor" /> < interceptor name ="execAndWait" class ="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor" /> < interceptor name ="exception" class ="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor" /> < interceptor name ="fileUpload" class ="org.apache.struts2.interceptor.FileUploadInterceptor" /> < interceptor name ="i18n" class ="com.opensymphony.xwork2.interceptor.I18nInterceptor" /> < interceptor name ="logger" class ="com.opensymphony.xwork2.interceptor.LoggingInterceptor" /> < interceptor name ="modelDriven" class ="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor" /> < interceptor name ="scopedModelDriven" class ="com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor" /> < interceptor name ="params" class ="com.opensymphony.xwork2.interceptor.ParametersInterceptor" /> < interceptor name ="actionMappingParams" class ="org.apache.struts2.interceptor.ActionMappingParametersInteceptor" /> < interceptor name ="prepare" class ="com.opensymphony.xwork2.interceptor.PrepareInterceptor" /> < interceptor name ="staticParams" class ="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor" /> < interceptor name ="scope" class ="org.apache.struts2.interceptor.ScopeInterceptor" /> < interceptor name ="servletConfig" class ="org.apache.struts2.interceptor.ServletConfigInterceptor" /> < interceptor name ="timer" class ="com.opensymphony.xwork2.interceptor.TimerInterceptor" /> < interceptor name ="token" class ="org.apache.struts2.interceptor.TokenInterceptor" /> < interceptor name ="tokenSession" class ="org.apache.struts2.interceptor.TokenSessionStoreInterceptor" /> < interceptor name ="validation" class ="org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor" /> < interceptor name ="workflow" class ="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor" /> < interceptor name ="store" class ="org.apache.struts2.interceptor.MessageStoreInterceptor" /> < interceptor name ="checkbox" class ="org.apache.struts2.interceptor.CheckboxInterceptor"/> < interceptor name ="profiling" class ="org.apache.struts2.interceptor.ProfilingActivationInterceptor" /> < interceptor name ="roles" class ="org.apache.struts2.interceptor.RolesInterceptor" /> < interceptor name ="annotationWorkflow" class ="com.opensymphony.xwork2.interceptor.annotations.AnnotationWorkflowInterceptor" /> < interceptor name ="multiselect" class ="org.apache.struts2.interceptor.MultiselectInterceptor" /> < interceptor name ="deprecation" class ="org.apache.struts2.interceptor.DeprecationInterceptor" /> <!-- Basic stack --> < interceptor-stack name ="basicStack" > < interceptor-ref name ="exception" /> < interceptor-ref name ="servletConfig" /> < interceptor-ref name ="prepare" /> < interceptor-ref name ="checkbox" /> < interceptor-ref name ="multiselect" /> < interceptor-ref name ="actionMappingParams" /> < interceptor-ref name ="params" > < param name ="excludeParams" > dojo..*,^struts..*,^session..*,^request..*,^application..*,^servlet(Request|Response)..*,parameters...* </ param> </ interceptor-ref> < interceptor-ref name ="conversionError" /> < interceptor-ref name ="deprecation" /> </ interceptor-stack> <!-- Sample validation and workflow stack --> < interceptor-stack name ="validationWorkflowStack" > < interceptor-ref name ="basicStack" /> < interceptor-ref name ="validation" /> < interceptor-ref name ="workflow" /> </ interceptor-stack> <!-- Sample file upload stack --> < interceptor-stack name ="fileUploadStack" > < interceptor-ref name ="fileUpload" /> < interceptor-ref name ="basicStack" /> </ interceptor-stack> <!-- Sample model-driven stack --> < interceptor-stack name ="modelDrivenStack" > < interceptor-ref name ="modelDriven" /> < interceptor-ref name ="basicStack" /> </ interceptor-stack> <!-- Sample action chaining stack --> < interceptor-stack name ="chainStack" > < interceptor-ref name ="chain" /> < interceptor-ref name ="basicStack" /> </ interceptor-stack> <!-- Sample i18n stack --> < interceptor-stack name ="i18nStack" > < interceptor-ref name ="i18n" /> < interceptor-ref name ="basicStack" /> </ interceptor-stack> <!-- An example of the paramsPrepareParams trick. This stack is exactly the same as the defaultStack, except that it includes one extra interceptor before the prepare interceptor: the params interceptor. This is useful for when you wish to apply parameters directly to an object that you wish to load externally (such as a DAO or database or service layer), but can't load that object until at least the ID parameter has been loaded. By loading the parameters twice, you can retrieve the object in the prepare() method, allowing the second params interceptor to apply the values on the object. --> < interceptor-stack name ="paramsPrepareParamsStack" > < interceptor-ref name ="exception" /> < interceptor-ref name ="alias" /> < interceptor-ref name ="i18n" /> < interceptor-ref name ="checkbox" /> < interceptor-ref name ="multiselect" /> < interceptor-ref name ="params" > < param name ="excludeParams" > ^dojo..*,^struts..*,^session..*,^request..*,^application..*,^servlet(Request|Response)..*,^parameters..*,^action:.*,^method:.* </ param> </ interceptor-ref> < interceptor-ref name ="servletConfig" /> < interceptor-ref name ="prepare" /> < interceptor-ref name ="chain" /> < interceptor-ref name ="modelDriven" /> < interceptor-ref name ="fileUpload" /> < interceptor-ref name ="staticParams" /> < interceptor-ref name ="actionMappingParams" /> < interceptor-ref name ="params" > < param name ="excludeParams" > ^dojo..*,^struts..*,^session..*,^request..*,^application..*,^servlet(Request|Response)..*,^parameters..*,^action:.*,^method:.* </ param> </ interceptor-ref> < interceptor-ref name ="conversionError" /> < interceptor-ref name ="validation" > < param name ="excludeMethods" > input,back,cancel,browse</ param > </ interceptor-ref> < interceptor-ref name ="workflow" > < param name ="excludeMethods" > input,back,cancel,browse</ param > </ interceptor-ref> </ interceptor-stack> <!-- A complete stack with all the common interceptors in place. Generally, this stack should be the one you use, though it may do more than you need. Also, the ordering can be switched around (ex: if you wish to have your servlet-related objects applied before prepare() is called, you'd need to move servletConfig interceptor up. This stack also excludes from the normal validation and workflow the method names input, back, and cancel. These typically are associated with requests that should not be validated. --> < interceptor-stack name ="defaultStack" > < interceptor-ref name ="exception" /> < interceptor-ref name ="alias" /> < interceptor-ref name ="servletConfig" /> < interceptor-ref name ="i18n" /> < interceptor-ref name ="prepare" /> < interceptor-ref name ="chain" /> < interceptor-ref name ="scopedModelDriven" /> < interceptor-ref name ="modelDriven" /> < interceptor-ref name ="fileUpload" /> < interceptor-ref name ="checkbox" /> < interceptor-ref name ="multiselect" /> < interceptor-ref name ="staticParams" /> < interceptor-ref name ="actionMappingParams" /> < interceptor-ref name ="params" > < param name ="excludeParams" > ^dojo..*,^struts..*,^session..*,^request..*,^application..*,^servlet(Request|Response)..*,^parameters..*,^action:.*,^method:.* </ param> </ interceptor-ref> < interceptor-ref name ="conversionError" /> < interceptor-ref name ="validation" > < param name ="excludeMethods" > input,back,cancel,browse</ param > </ interceptor-ref> < interceptor-ref name ="workflow" > < param name ="excludeMethods" > input,back,cancel,browse</ param > </ interceptor-ref> < interceptor-ref name ="debugging" /> < interceptor-ref name ="deprecation" /> </ interceptor-stack> <!-- The completeStack is here for backwards compatibility for applications that still refer to the defaultStack by the old name --> < interceptor-stack name ="completeStack" > < interceptor-ref name ="defaultStack" /> </ interceptor-stack> <!-- Sample execute and wait stack. Note: execAndWait should always be the *last* interceptor. --> < interceptor-stack name ="executeAndWaitStack" > < interceptor-ref name ="execAndWait" > < param name ="excludeMethods" > input,back,cancel</ param > </ interceptor-ref> < interceptor-ref name ="defaultStack" /> < interceptor-ref name ="execAndWait" > < param name ="excludeMethods" > input,back,cancel</ param > </ interceptor-ref> </ interceptor-stack> </ interceptors> < default-interceptor-ref name ="defaultStack" /> < default-class-ref class ="com.opensymphony.xwork2.ActionSupport" /> </package > </ struts>
我们能看到这里有一个
< interceptor name = "i18n" class = "com.opensymphony.xwork2.interceptor.I18nInterceptor" />,可见i18n拦截的实现类是com.opensymphony.xwork2.interceptor.I18nInterceptor。现在我们找到这个而实现类
/* * Copyright 2002-2006,2009 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.opensymphony.xwork2.interceptor; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.util.LocalizedTextUtil; import com.opensymphony.xwork2.util.logging.Logger; import com.opensymphony.xwork2.util.logging.LoggerFactory; import java.util.Locale; import java.util.Map; /** * <!-- START SNIPPET: description --> * <p/> * An interceptor that handles setting the locale specified in a session as the locale for the current action request. * In addition, this interceptor will look for a specific HTTP request parameter and set the locale to whatever value is * provided. This means that this interceptor can be used to allow for your application to dynamically change the locale * for the user's session or, alternatively, only for the current request (since XWork 2.1.3). * This is very useful for applications that require multi-lingual support and want the user to * be able to set his or her language preference at any point. The locale parameter is removed during the execution of * this interceptor, ensuring that properties aren't set on an action (such as request_locale) that have no typical * corresponding setter in your action. * <p/> * <p/>For example, using the default parameter name, a request to <b>foo.action?request_locale=en_US</b>, then the * locale for US English is saved in the user's session and will be used for all future requests. * <p/> if there is no locale set (for example with the first visit), the interceptor uses the browser locale. * <p/> * <!-- END SNIPPET: description --> * <p/> * <p/> <u>Interceptor parameters:</u> * <p/> * <!-- START SNIPPET: parameters --> * <p/> * <ul> * <p/> * <li>parameterName (optional) - the name of the HTTP request parameter that dictates the locale to switch to and save * in the session. By default this is <b>request_locale</b></li> * <p/> * <li>requestOnlyParameterName (optional) - the name of the HTTP request parameter that dictates the locale to switch to * for the current request only, without saving it in the session. By default this is <b>request_only_locale</b></li> * <p/> * <li>attributeName (optional) - the name of the session key to store the selected locale. By default this is * <b>WW_TRANS_I18N_LOCALE</b></li> * <p/> * </ul> * <p/> * <!-- END SNIPPET: parameters --> * <p/> * <p/> <u>Extending the interceptor:</u> * <p/> * <p/> * <p/> * <!-- START SNIPPET: extending --> * <p/> * There are no known extensions points for this interceptor. * <p/> * <!-- END SNIPPET: extending --> * <p/> * <p/> <u>Example code:</u> * <p/> * <pre> * <!-- START SNIPPET: example --> * <action name="someAction" class="com.examples.SomeAction"> * <interceptor-ref name="i18n"/> * <interceptor-ref name="basicStack"/> * <result name="success">good_result.ftl</result> * </action> * <!-- END SNIPPET: example --> * </pre> * * @author Aleksei Gopachenko */ public class I18nInterceptor extends AbstractInterceptor { private static final long serialVersionUID = 2496830135246700300L; protected static final Logger LOG = LoggerFactory.getLogger(I18nInterceptor.class); public static final String DEFAULT_SESSION_ATTRIBUTE = "WW_TRANS_I18N_LOCALE"; public static final String DEFAULT_PARAMETER = "request_locale"; public static final String DEFAULT_REQUESTONLY_PARAMETER = "request_only_locale"; protected String parameterName = DEFAULT_PARAMETER; protected String requestOnlyParameterName = DEFAULT_REQUESTONLY_PARAMETER; protected String attributeName = DEFAULT_SESSION_ATTRIBUTE; // Request-Only = None protected enum Storage { SESSION, NONE } public I18nInterceptor() { if (LOG.isDebugEnabled()) { LOG.debug("new I18nInterceptor()"); } } public void setParameterName(String parameterName) { this.parameterName = parameterName; } public void setRequestOnlyParameterName(String requestOnlyParameterName) { this.requestOnlyParameterName = requestOnlyParameterName; } public void setAttributeName(String attributeName) { this.attributeName = attributeName; } @Override public String intercept(ActionInvocation invocation) throws Exception { if (LOG.isDebugEnabled()) { LOG.debug("intercept '#0/#1' {", invocation.getProxy().getNamespace(), invocation.getProxy().getActionName()); } LocaleFinder localeFinder = new LocaleFinder(invocation); Locale locale = getLocaleFromParam(localeFinder.getRequestedLocale()); locale = storeLocale(invocation, locale, localeFinder.getStorage()); saveLocale(invocation, 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; } /** * Store the locale to the chosen storage, like f. e. the session * * @param invocation the action invocation * @param locale the locale to store * @param storage the place to store this locale (like Storage.SESSSION.toString()) */ protected Locale storeLocale(ActionInvocation invocation, Locale locale, String storage) { //save it in session Map<String, Object> session = invocation.getInvocationContext().getSession(); if (session != null) { synchronized (session) { if (locale == null) { storage = Storage.NONE.toString(); locale = readStoredLocale(invocation, session); } if (Storage.SESSION.toString().equals(storage)) { session.put(attributeName, locale); } } } return locale; } protected class LocaleFinder { protected String storage = Storage.SESSION.toString(); protected Object requestedLocale = null; protected ActionInvocation actionInvocation = null; protected LocaleFinder(ActionInvocation invocation) { actionInvocation = invocation; find(); } protected void find() { //get requested locale Map<String, Object> params = actionInvocation.getInvocationContext().getParameters(); storage = Storage.SESSION.toString(); requestedLocale = findLocaleParameter(params, parameterName); if (requestedLocale != null) { return; } requestedLocale = findLocaleParameter(params, requestOnlyParameterName); if (requestedLocale != null) { storage = Storage.NONE.toString(); } } public String getStorage() { return storage; } public Object getRequestedLocale() { return requestedLocale; } } /** * Creates a Locale object from the request param, which might * be already a Local or a String * * @param requestedLocale the parameter from the request * @return the Locale */ protected Locale getLocaleFromParam(Object requestedLocale) { Locale locale = null; if (requestedLocale != null) { 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; } /** * Reads the locale from the session, and if not found from the * current invocation (=browser) * * @param invocation the current invocation * @param session the current session * @return the read locale */ protected Locale readStoredLocale(ActionInvocation invocation, Map<String, Object> session) { Locale locale = this.readStoredLocalFromSession(invocation, session); if (locale != null) { return locale; } return this.readStoredLocalFromCurrentInvocation(invocation); } protected Locale readStoredLocalFromSession(ActionInvocation invocation, Map<String, Object> session) { // check session for saved locale Object sessionLocale = session.get(attributeName); if (sessionLocale != null && sessionLocale instanceof Locale) { Locale locale = (Locale) sessionLocale; if (LOG.isDebugEnabled()) { LOG.debug("applied session locale=#0", locale); } return locale; } return null; } protected Locale readStoredLocalFromCurrentInvocation(ActionInvocation invocation) { // no overriding locale definition found, stay with current invocation (=browser) locale Locale locale = invocation.getInvocationContext().getLocale(); if (locale != null && LOG.isDebugEnabled()) { LOG.debug("applied invocation context locale=#0", locale); } return locale; } protected Object findLocaleParameter(Map<String, Object> params, String parameterName) { Object requestedLocale = params.remove(parameterName); if (requestedLocale != null && requestedLocale.getClass().isArray() && ((Object[]) requestedLocale).length == 1) { requestedLocale = ((Object[]) requestedLocale)[0]; if (LOG.isDebugEnabled()) { LOG.debug("requested_locale=#0", requestedLocale); } } return requestedLocale; } /** * Save the given locale to the ActionInvocation. * * @param invocation The ActionInvocation. * @param locale The locale to save. */ protected void saveLocale(ActionInvocation invocation, Locale locale) { invocation.getInvocationContext().setLocale(locale); } }
该拦截器有三个参数,parameterName,requestOnlyParameterName,attributeName;前两个是设置用户语言环境参数的name值,最后一个是设置session中保存语言环境对象的key值;三者的默认值分别为:request_locale,request_only_locale,WW_TRANS_I18N_LOCALE。现在在看看前面的JSP页面,你会发现我们用get方式传递的参数,参数名就是request_locale。 用户请求中request_locale参数的优先级大于request_only_locale,语言环境是从request_locale参数中获得,该语言环境对象将会以key值为WW_TRANS_I18N_LOCALE保存到session中,语言环境是从request_only_locale总获得,设置值在当前请求中起效。请求中不存在request_locale和request_only_locale参数,将会从session中获取key值为WW_TRANS_I18N_LOCALE的对象,若该对象为null或者不是Locale类型,将使用ActionContext.getLocale()方法获取语言环境。当执行完以上一系列逻辑后,将会把当前上下文的语言环境设置为获取到的对象。