Using resource bundle for validation messages in Spring

本文探讨了在Spring MVC中实现国际化配置时遇到的问题及解决方案,重点介绍了如何正确配置消息来源,解决找不到国际化消息的问题,并提供了动态加载多语言资源文件的方法。
What i am trying to do is
1. putting up the messages file in WEB-INF/i18/validationMessages_en.properties and trying to validate the messages on login.

The dispatcher-servlet.xml contains
  1. <context:component-scanbase-package="com.practise.controller"/>
  2. <beanid="userValidator"class="com.practise.validator.UserValidator">
  3. </bean>
  4. <beanclass="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
  5. <beanclass="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
  6. <beanid="viewResolver"class="org.springframework.web.servlet.view.UrlBasedViewResolver">
  7. <propertyname="viewClass"value="org.springframework.web.servlet.view.JstlView"/>
  8. <propertyname="prefix"value="/WEB-INF/jsp/"/>
  9. <propertyname="suffix"value=".jsp"/>
  10. </bean>
  11. <beanid="messageSource"class="org.springframework.context.support.ResourceBundleMessageSource">
  12. <propertyname="basenames"value="/WEB-INF/i18n/validationMessages_en">
  13. </property>
  14. </bean>


  1. @RequestMapping(value="/login",method=RequestMethod.GET)
  2. publicModelAndViewauthenticateUser(HttpServletRequestrequest,
  3. HttpServletResponseresponse,
  4. @ModelAttribute("loginbean")LoginBeanloginbean,
  5. BindingResultresult){
  6. ModelMapobjModel=newModelMap();
  7. userValidator.validate(loginbean,result);
  8. objModel.addAttribute("loginbean",loginbean);
  9. if(result.hasErrors()){
  10. returnnewModelAndView("home",objModel);
  11. }
  12. returnnewModelAndView("practise",objModel);
  13. }


the valdation class

  1. publicclassUserValidatorimplementsValidator{
  2. @Override
  3. publicbooleansupports(ClassaClass){
  4. returnLoginBean.class.equals(aClass);
  5. }
  6. @Override
  7. publicvoidvalidate(Objectarg0,Errorserrors){
  8. ValidationUtils.rejectIfEmptyOrWhitespace(errors,"username","username.required");
  9. ValidationUtils.rejectIfEmptyOrWhitespace(errors,"password","password.required");
  10. }



validationMessages.properties
  1. username.required=UserNameisarequiredfield.
  2. password.required=Passwordisarequiredfield.


below is the error console
org.springframework.context.NoSuchMessageException: No message found under code 'username.required' for locale 'en_US'.
at org.springframework.context.support.AbstractMessageSource.getMessage(AbstractMessageSource.java:161)
at org.springframework.context.support.AbstractApplicationContext.getMessage(AbstractApplicationContext.java:1196)
at org.springframework.web.servlet.support.RequestContext.getMessage(RequestContext.java:553)
at org.springframework.web.servlet.support.BindStatus.initErrorMessages(BindStatus.java:177)
at org.springframework.web.servlet.support.BindStatus.getErrorMessages(BindStatus.java:273)
at org.springframework.web.servlet.tags.form.ErrorsTag.exposeAttributes(ErrorsTag.java:173)
at org.springframework.web.servlet.tags.form.AbstractHtmlElementBodyTag.writeTagContent(AbstractHtmlElementBodyTag.java:48)
at org.springframework.web.servlet.tags.form.AbstractFormTag.doStartTagInternal(AbstractFormTag.java:102)
at org.springframework.web.servlet.tags.RequestContextAwareTag.doStartTag(RequestContextAwareTag.java:79)
at org.apache.jsp.WEB_002dINF.jsp.home_jsp._jspService(home_jsp.java:112)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:377)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:313)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:260)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:646)
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:436)
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:374)
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:302)
at org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:238)
at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:250)
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1047)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:817)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:549)

______________________________________________________________
But when i put my properties file under src folder and change my dispatcher-servelet as
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames" value="validationMessages_en">
</property>
</bean

all seems to work fine.

What may be the problem if i use i18n and put my properties file under this folder


Thanks Jaspreet ----- Nothing is permanent

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

I would like to mimic the Grails way of resolving i18n messages.

In WEB-INF/i18n/ I have the following directories:

admin/messages_EN.properties

admin/messages_FR.properties

website/messages_EN.properties

website/messages_FR.properties

please ignore the language endings ( EN and FR ) in this example

in my xml configuration I currently have:

<!-- Register the welcome.properties -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
 <property name="defaultEncoding" value="utf-8" />
 <property name="basename" value="/WEB-INF/i18n/" />
</bean>

What I am looking for here, is a way to tell Spring to look for .properties files under i18n but without explicitly telling it what each subdirectory is. That is without a list of basenames that points to /WEB-INF/i18n/admin/ and /WEB-INF/i18n/website/

I want the WEB-INF/i18n/ directory to be dynamic, and that bundles ( directories ) can be created without having to remodify the xml configuration file.

I am not trying to solve this particular example with admin and website sub directories

Is this possible?

Thanks!

share | improve this question

54% accept rate
You'll probably have to extend ReloadableResourceBundleMessageSource to introduce some sort of discover property that causes the message source to search the basename directory, instead of loading files directly.Paul Grime Jun 20 at 10:51
@PaulGrime I have implemented something based on your suggestion and edited my question to include the suggested implementation. Something along these lines should work right? Missing something? Thanks!Hamidam Jun 20 at 15:17
feedback

1 Answer

up vote 0 down vote accepted

Here is the solution:

package com.mypackage.core.src;

import java.io.File;
import java.util.ArrayList;

import javax.servlet.ServletContext;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;

public class UnderDirectoryReloadableResourceBundleMessageSource extends ReloadableResourceBundleMessageSource {

  @Autowired
  ServletContext servletContext;

  public void setWorkingDirectory(String directoryPath) {
    File rootDir = new File( servletContext.getRealPath(directoryPath) );
    ArrayList<String> baseNames = new ArrayList<String>();
    iterateScanDirectoryAndAddBaseNames(baseNames, rootDir);
    setBasenames(baseNames.toArray(new String[baseNames.size()]));
  }

  private void iterateScanDirectoryAndAddBaseNames(ArrayList<String> baseNames, File directory) {
    File[] files = directory.listFiles();

    for (File file : files) {
      if (file.isDirectory()) {
        iterateScanDirectoryAndAddBaseNames(baseNames, file);
      } else {
        if (file.getName().endsWith(".properties")) {
          String filePath = file.getAbsolutePath().replaceAll("\\\\", "/").replaceAll(".properties$", "");
          filePath = filePath.substring(filePath.indexOf("/WEB-INF/"), filePath.length());
          baseNames.add(filePath);
          System.out.println("Added file to baseNames: " + filePath);
        }
      }
    }
  }

}

XML config:

<bean id="messageSource" class="com.mypackage.core.src.UnderDirectoryReloadableResourceBundleMessageSource">
 <property name="defaultEncoding" value="utf-8" />
 <property name="workingDirectory" value="/WEB-INF/webspring/i18n" />
 <property name="cacheSeconds" value="3" />
 <property name="fallbackToSystemLocale" value="false" />
</bean>

Enjoy!





在IT领域中,**资源包(Resource Bundle)** 是实现 **国际化(i18n)** 和 **本地化(l10n)** 的核心机制之一。资源包本质上是一组键值对,用于存储特定语言或区域的文本、图像、格式等资源。通过资源包,开发者可以将应用程序的用户界面和内容适配到不同的语言和文化环境中,从而提升全球用户的使用体验。 ### 资源包的基本配置 在Java中,`ResourceBundle` 是标准库中用于管理资源包的核心类。开发者通常会为每种语言创建一个属性文件(如 `messages_en.properties`、`messages_zh.properties`),并通过 `ResourceBundle.getBundle()` 方法加载对应的资源。例如: ```java ResourceBundle bundle = ResourceBundle.getBundle("messages", Locale.CHINA); String greeting = bundle.getString("greeting"); ``` 其中,`messages_zh.properties` 文件可能包含: ``` greeting=你好,欢迎使用本系统。 ``` 而对应的英文文件 `messages_en.properties` 则为: ``` greeting=Hello, welcome to the system. ``` 这种方式使得应用程序能够根据用户的区域设置自动选择合适的语言资源[^1]。 ### 国际化与本地化的实现 国际化是指设计应用程序时使其能够支持多种语言和区域设置,而本地化则是将这些通用资源适配为特定语言和文化的过程。资源包在这一过程中起到了关键作用。例如,在Flutter框架中,使用 **ARB(Application Resource Bundle)** 格式来管理多语言资源。开发者需要在 `lib/l10n` 目录下创建如 `app_en.arb` 和 `app_fr.arb` 的资源文件,分别对应英语和法语[^4]。 ### 资源管理与错误处理 在资源包的使用过程中,常见的问题包括资源缺失、区域设置不匹配、文件格式错误等。为了避免这些问题,可以采取以下措施: - **使用默认资源文件**:例如 `messages.properties` 作为默认的资源文件,当没有匹配的区域文件时使用。 - **资源键的统一管理**:确保所有资源键在不同语言文件中保持一致,避免运行时异常。 - **使用工具自动化处理**:例如在Flutter中使用 `intl_utils` 包,可以自动生成本地化代码,并与翻译平台(如 Localizely)集成,减少手动维护的负担[^5]。 ### 使用指南与最佳实践 1. **命名规范**:资源文件应按照语言和区域命名,例如 `messages_en_US.properties` 表示美式英语。 2. **区域设置检测**:应用程序应能自动检测用户的区域设置,或允许用户手动选择语言。 3. **避免硬编码文本**:所有用户界面文本都应从资源包中获取,而不是直接写在代码中。 4. **测试不同语言环境**:确保应用程序在不同语言下都能正常显示和运行,特别是对于从右到左的语言(如阿拉伯语)。 5. **使用占位符处理动态内容**:例如 `"welcome_message=欢迎,{0}!"` 可以通过 `MessageFormat.format(bundle.getString("welcome_message"), username)` 动态插入用户名。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值