作为SpringMVC中URL一部分的区域设置
我现在正在寻找一个多语言web应用程序的框架。目前,在我看来,最好的选择是SpringMVC。但我面对的事实是,所有针对开发人员的指导原则都建议以这种方式使用LocaleChangeInterceptor切换语言: 不幸的是,我想避免这样做的原因有很多。我怎样才能使语言代码成为URL的重要组成部分?例如: 谢谢 UPD:我找到了以下解决方案。它还没有完成,但很有效。解决方案由两部分组成——servlet过滤器和区域设置解析器bean。这看起来有点不妥,但我看不到其他方法来解决这个问题作为SpringMVC中URL一部分的区域设置,url,spring-mvc,locale,Url,Spring Mvc,Locale,我现在正在寻找一个多语言web应用程序的框架。目前,在我看来,最好的选择是SpringMVC。但我面对的事实是,所有针对开发人员的指导原则都建议以这种方式使用LocaleChangeInterceptor切换语言: 不幸的是,我想避免这样做的原因有很多。我怎样才能使语言代码成为URL的重要组成部分?例如: 谢谢 UPD:我找到了以下解决方案。它还没有完成,但很有效。解决方案由两部分组成——servlet过滤器和区域设置解析器bean。这看起来有点不妥,但我看不到其他方法来解决这个问题 pub
public class LocaleFilter implements Filter
{
...
private static final String DEFAULT_LOCALE = "en";
private static final String[] AVAILABLE_LOCALES = new String[] {"en", "ru"};
public LocaleFilter() {}
private List<String> getSevletRequestParts(ServletRequest request)
{
String[] splitedParts = ((HttpServletRequest) request).getServletPath().split("/");
List<String> result = new ArrayList<String>();
for (String sp : splitedParts)
{
if (sp.trim().length() > 0)
result.add(sp);
}
return result;
}
private Locale getLocaleFromRequestParts(List<String> parts)
{
if (parts.size() > 0)
{
for (String lang : AVAILABLE_LOCALES)
{
if (lang.equals(parts.get(0)))
{
return new Locale(lang);
}
}
}
return null;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException
{
List<String> requestParts = this.getSevletRequestParts(request);
Locale locale = this.getLocaleFromRequestParts(requestParts);
if (locale != null)
{
request.setAttribute(LocaleFilter.class.getName() + ".LOCALE", locale);
StringBuilder sb = new StringBuilder();
for (int i = 1; i < requestParts.size(); i++)
{
sb.append('/');
sb.append((String) requestParts.get(i));
}
RequestDispatcher dispatcher = request.getRequestDispatcher(sb.toString());
dispatcher.forward(request, response);
}
else
{
request.setAttribute(LocaleFilter.class.getName() + ".LOCALE", new Locale(DEFAULT_LOCALE));
chain.doFilter(request, response);
}
}
...
}
public class FilterLocaleResolver implements LocaleResolver
{
private Locale DEFAULT_LOCALE = new Locale("en");
@Override
public Locale resolveLocale(HttpServletRequest request)
{
Locale locale = (Locale) request.getAttribute(LocaleFilter.class.getName() + ".LOCALE");
return (locale != null ? locale : DEFAULT_LOCALE);
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale)
{
request.setAttribute(LocaleFilter.class.getName() + ".LOCALE", locale);
}
}
在Spring3.0中,您可以告诉控制器查找。e、 g
我使用过滤器和拦截器的组合实现了一些非常类似的东西 过滤器提取第一个path变量,如果它是有效的语言环境,则将其设置为请求属性,将其从请求的URI的开头剥离,并将请求转发到新的URI
public class PathVariableLocaleFilter extends OncePerRequestFilter {
private static final Logger LOG = LoggerFactory.getLogger(PathVariableLocaleFilter.class);
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String url = defaultString(request.getRequestURI().substring(request.getContextPath().length()));
String[] variables = url.split("/");
if (variables.length > 1 && isLocale(variables[1])) {
LOG.debug("Found locale {}", variables[1]);
request.setAttribute(LOCALE_ATTRIBUTE_NAME, variables[1]);
String newUrl = StringUtils.removeStart(url, '/' + variables[1]);
LOG.trace("Dispatching to new url \'{}\'", newUrl);
RequestDispatcher dispatcher = request.getRequestDispatcher(newUrl);
dispatcher.forward(request, response);
} else {
filterChain.doFilter(request, response);
}
}
private boolean isLocale(String locale) {
//validate the string here against an accepted list of locales or whatever
try {
LocaleUtils.toLocale(locale);
return true;
} catch (IllegalArgumentException e) {
LOG.trace("Variable \'{}\' is not a Locale", locale);
}
return false;
}
}
拦截器与LocaleChangeInterceptor
非常相似,它尝试从请求属性获取区域设置,如果找到区域设置,则将其设置为LocaleResolver
public class LocaleAttributeChangeInterceptor extends HandlerInterceptorAdapter {
public static final String LOCALE_ATTRIBUTE_NAME = LocaleAttributeChangeInterceptor.class.getName() + ".LOCALE";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
Object newLocale = request.getAttribute(LOCALE_ATTRIBUTE_NAME);
if (newLocale != null) {
LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
if (localeResolver == null) {
throw new IllegalStateException("No LocaleResolver found: not in a DispatcherServlet request?");
}
localeResolver.setLocale(request, response, StringUtils.parseLocaleString(newLocale.toString()));
}
// Proceed in any case.
return true;
}
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LocaleAttributeChangeInterceptor());
}
@Bean(name = "localeResolver")
public LocaleResolver getLocaleResolver() {
return new CookieLocaleResolver();
}
@Override
protected Filter[] getServletFilters() {
return new Filter[] { new PathVariableLocaleFilter() };
}
一旦它们就位,您需要将Spring配置为使用拦截器和LocaleResolver
public class LocaleAttributeChangeInterceptor extends HandlerInterceptorAdapter {
public static final String LOCALE_ATTRIBUTE_NAME = LocaleAttributeChangeInterceptor.class.getName() + ".LOCALE";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
Object newLocale = request.getAttribute(LOCALE_ATTRIBUTE_NAME);
if (newLocale != null) {
LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
if (localeResolver == null) {
throw new IllegalStateException("No LocaleResolver found: not in a DispatcherServlet request?");
}
localeResolver.setLocale(request, response, StringUtils.parseLocaleString(newLocale.toString()));
}
// Proceed in any case.
return true;
}
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LocaleAttributeChangeInterceptor());
}
@Bean(name = "localeResolver")
public LocaleResolver getLocaleResolver() {
return new CookieLocaleResolver();
}
@Override
protected Filter[] getServletFilters() {
return new Filter[] { new PathVariableLocaleFilter() };
}
并将过滤器添加到抽象注释配置Dispatchers ServletInitializer
public class LocaleAttributeChangeInterceptor extends HandlerInterceptorAdapter {
public static final String LOCALE_ATTRIBUTE_NAME = LocaleAttributeChangeInterceptor.class.getName() + ".LOCALE";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
Object newLocale = request.getAttribute(LOCALE_ATTRIBUTE_NAME);
if (newLocale != null) {
LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
if (localeResolver == null) {
throw new IllegalStateException("No LocaleResolver found: not in a DispatcherServlet request?");
}
localeResolver.setLocale(request, response, StringUtils.parseLocaleString(newLocale.toString()));
}
// Proceed in any case.
return true;
}
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LocaleAttributeChangeInterceptor());
}
@Bean(name = "localeResolver")
public LocaleResolver getLocaleResolver() {
return new CookieLocaleResolver();
}
@Override
protected Filter[] getServletFilters() {
return new Filter[] { new PathVariableLocaleFilter() };
}
我还没有完全测试过它,但它似乎到目前为止还可以工作,而且您不必触摸控制器就可以接受
{locale}
path变量,它应该可以开箱即用。也许将来我们会有“locale as path variable/subfolder”的Spring automagic解决方案,因为似乎越来越多的网站都在采用它,而且据一些网站介绍。我发现自己也遇到了同样的问题,在做了大量的研究之后,我最终还使用了一个过滤器和LocaleResolver。步骤指南的步骤:
首先在web.xml中设置过滤器:
<filter>
<filter-name>LocaleFilter</filter-name>
<filter-class>yourCompleteRouteToTheFilter.LocaleUrlFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LocaleFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
现在,过滤器向请求注入了两个属性,我们将使用这两个属性来形成区域设置,并从url中剥离语言以正确处理请求。现在我们将定义一个LocaleResolver来更改区域设置。首先,我们修改我们的servlet.xml文件:
<!-- locale Resolver configuration-->
<bean id="localeResolver" class="yourCompleteRouteToTheResolver.CustomLocaleResolver"></bean>
执行此操作时,您无需更改控制器中的任何内容,访问“/en/home”与访问“/home”和使用语言_en.properties文件相同。希望能有所帮助我最近遇到了同样的问题。所以我希望无状态语言环境不依赖于会话、cookie或其他任何东西,而只是URL 我尝试了前面答案中建议的filter/interceptor/localeResolver解决方案,但这些解决方案不符合我的需要:
- 静态内容(图像等)
- 页面部分不依赖于区域设置(管理面板)
- RestController在同一个应用程序中
- 多部分文件上传器
@Controller
@RequestMapping(path = "/{lang}")
public class LanguageAwareController {
@Autowired
LocaleResolver localeResolver;
@ModelAttribute(name = "locale")
Locale getLocale(@PathVariable(name = "lang") String lang, HttpServletRequest request,
HttpServletResponse response){
Locale effectiveLocale = Arrays.stream(Locale.getAvailableLocales())
.filter(locale -> locale.getLanguage().equals(lang))
.findFirst()
.orElseGet(Locale::getDefault);
localeResolver.setLocale(request, response, effectiveLocale);
return effectiveLocale;
}
}
在其中一个控制器中使用:
@Controller
public class LandingPageController extends LanguageAwareController{
private Log log = LogFactory.getLog(LandingPageController.class);
@GetMapping("/")
public String welcomePage(Locale locale, @PathVariable(name = "lang") String lang ){
log.info(lang);
log.info(locale);
return "landing";
}
}
除了提供的答案之外,这里还有一种方法,可以通过实现
ILinkBuilder
,让Thymeleaf在一个又一个上下文路径中自动预先设置语言环境:
@Bean
公共ILinkBuilder路径变量localeLinkBuilder(){
PathVariableLocaleLinkBuilder PathVariableLocaleLinkBuilder=新的PathVariableLocaleLinkBuilder();
pathVariableLocaleLinkBuilder.setOrder(1);
返回pathVariableLocaleLinkBuilder;
}
@豆子
SpringTemplateEngine templateEngine(TeleLabProperties属性、ObjectProvider TemplateResolver、ObjectProvider方言、ObjectProvider链接生成器){
SpringTemplateEngine=新的SpringTemplateEngine();
setEnableSpringELCompiler(properties.isEnableSpringElCompiler());
engine.setRenderHiddenMarkersBefore复选框(properties.isRenderHiddenMarkersBefore复选框());
forEach(引擎::addTemplateResolver);
dialogons.orderedStream().forEach(引擎::adddialogue);
linkBuilders.orderedStream().forEach(引擎::addLinkBuilder);
返回引擎;
}
以下是链接生成器本身:
公共类PathVariableLocaleLinkBuilder扩展了AbstractLinkBuilder{
@自动连线
私有LocaleResolver-LocaleResolver;
@凌驾
公共字符串构建链接(IExpressionContext上下文、字符串基、映射参数){
notNull(上下文,“表达式上下文不能为null”);
if(base==null){
返回null;
}
如果(!IsLink BaseContextRelative(基本)){
返回基地;
}
if(!(IWebContext的上下文实例)){
抛出新的TemplateProcessingException(
链接基\“+base+”\”不能是上下文相关(/…),除非上下文+
用于执行引擎实现“+IWebContext.class.getName()+”接口”);
}
最终HttpServletRequest请求=((IWebContext)上下文).getRequest();
返回“/”+localeResolver.resolveLocale(请求)+base;
}
私有静态布尔值isLinkBaseContextRelative(最终字符序列linkBase){
if(linkBase.length()==0 | | linkBase.charAt(0)!='/')){
返回false;
}
返回linkBase.length()==1 | | linkBase.charAt(1)!='/';
}
}
但在这种情况下,我必须在每个操作中手动切换区域设置。是否可以进行自动切换,例如,使用过滤器?Hi@Gris。。。哈夫