item=상품
item.id=상품 ID
item.itemName=상품명
item.price=가격
item.quantity=수량
메시지
라고 한다.<label for="itemName" th:text="#{item.itemName}"></label>
메시지
를 여러 문자로 객체를 만들어두고 item=Item
item.id=Item ID
item.itemName=Item Name
item.price=price
item.quantity=quantity
사용자 요청의 HTTP accept-language 해더
를 확인하여 요구사항에 맞게 메시지 객체를 넣어주면 된다.
물론 사용자가 직접 언어를 선택하게 할 수 있도록 하면 더 좋다.
하지만 스프링은 이러한 메시지, 국제화 기능을 모두 제공한다. 심지어 thymeleaf오 스프링이 제공하는 메시지, 국제화 기능을 편리하게 통합하여 제공한다.
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasenames("messages", "errors");
messageSource.setDefaultEncoding("utf-8");
return messageSource;
}
원래라면 사용자가 이 메시지를 관리해주는, 스프링이 제공하는 MessageSource빈
을 등록을 해줘야한다. 하지만 스프링이 알아서 등록을 해둔다.
참고록 MessageSource
는 인터페이스
라 이를 구현한 구현체인 ResourceBundleMessageSource
를 등록해줘야한다.
basenames : 설정 파일의 이름을 지정한다.
defaultEncoding : 인코딩 정보를 지정한다. utf-8 을 사용하면 된다.
spring.messages.basename=messages,config.i18n.messages
코드를 application.properties
에 추가하면 메시지 소스를 설정할 수 있다.
참고로 default는 spring.messages.basename=messages
이다.
더 자세한 옵션들은 공식문서를 확인하면 된다.
MessageSource 를 스프링 빈으로 등록하지 않고, 스프링 부트와 관련된 별도의 설정을 하지 않으면 messages 라
는 이름으로 기본 등록된다. 따라서 messages_en.properties , messages_ko.properties ,
messages.properties 파일만 등록하면 자동으로 인식된다.
messages.properties
파일을 만들어주면 이게 default로 나가고 추가적으로 messages_en.properties
을 만들면 IDE가 알아서 bundle 폴더를 만들어준다. messages.properties :기본 값으로 사용(한글)
messages_en.properties : 영어 국제화 사용
참고로 파일명은 메시지가 아니라 메시지쓰
다!
그리고 얘가 앞서 작성한 @Bean
으로 등록되기 때문에 이제 우리는 얘를 갖다가 쓰는 방법에 대해 알아보자.
@SpringBootTest
public class MessageSourceTest {
@Autowired
MessageSource messageSource;
@Test
void helloMessage() {
String result = messageSource.getMessage("hello", null, null);
assertThat(result).isEqualTo("hello");
}
}
간단한 테스트 작성을 통하여 확인할 수 있다 (참고로 시스템 설정의 언어가 이렇게 영어로 되어있다면 영어로 나올테니 그것만 인지하자.)
ms.getMessage(, , )
메서드는 순서대로 code: hello
, args: null
, locale: null
를 받는다.
args
는 hello=안녕 hello.name=안녕 {0}
에서 {0}
에 들어가는 값이다.
locale
정보가 없으면 basename
에서 설정한 기본 이름 메시지 파일을 조회한다.
앞서 application.properties
에서 basename
으로 messages
를 지정 했으므로 messages.properties
파일에서 데이터를 조회한다.
@Test
void notFoundMessageCode() {
assertThatThrownBy(() -> messageSource.getMessage("no_code", null, null))
.isInstanceOf(NoSuchMessageException.class);
}
@Test
void notFoundMessageCodeDefaultMessage() {
String result = messageSource.getMessage("no_code", null, "default message", null);
assertThat(result).isEqualTo("default message");
}
hello=hello
hello.name=hello {0}
label.item=product
label.item.id=product ID
label.item.itemName=product name
label.item.price=prrice
label.item.quantity=quantity
page.items=product list
page.item=product detail
page.addItem=product register
page.updateItem=product edit
button.save=save
button.cancel=canle
hello=안녕
hello.name=안녕 {0}
그래서 이렇게 각각의 속성 파일이 있다고 가정할 때
@Test
void argumentMessage() {
String message = messageSource.getMessage("hello.name", new Object[]{"spring message"}, null);
assertThat(message).isEqualTo("hello spring message");
}
{0}
여기에 사용자가 작성한 "spring message"라는 문자열이 들어가기 때문에 위 테스트 코드는 성공적으로 실행된다. @Test
void defaultLang() {
assertThat(messageSource.getMessage("hello", null, null)).isEqualTo("hello");
assertThat(messageSource.getMessage("hello", null, Locale.KOREA)).isEqualTo("안녕");
}
#{}
을 통하여 편리하게 메시지를 조회할 수 있다.<div class="py-5 text-center">
<h2 th:text="#{page.addItem}">상품 등록 폼</h2>
</div>
hello.name=안녕 {0}
<p th:text="#{hello.name(${item.itemName})}"></p>
사실 이렇게 작성해두면 chrome에서는 설정에 들어가서 사용자 언어를 영어를 1순위로 올리든지 한국어를 1순위로 올리든지 하면 언어가 자동으로 바뀐다. 하지만 사용자가 chrome 설정에 들어가서 바꾸라고 하면 너무 무책임 하기 때문에
개발자는 쿠키나 세션에 사용자 언어 설정을 보관해두고 해당 언어로 locale을 지정해주면 된다.
그리고 이를 도와주는 것이 바로 localeResolver이다.
LocaleResolver
라는 인터페이스를 제공하는데,AcceptHeaderLocaleResolver
를 사용한다.LocaleResolver 인터페이스
public interface LocaleResolver {
Locale resolveLocale(HttpServletRequest request);
void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale);
}