minOS

메세지,국제화 본문

728x90

메세지 

어떤 기획자가 상품명이라는 단어를 모두 상품이름으로 고쳐달라고 하면 어떻게 해야할까?
lable에 있는 단어를 변경하려면 모든 화면에서 그 단어를 찾아 변경해야한다.
왜냐하면 해당 HTML 파일에 메시지가 하드코딩 되어 있기 때문이다.


1)messages.properties 라는 파일 만들기

hello= 안녕
hello.name =안녕 {0}
label.item=상품
label.item.id=상품 ID
label.item.itemName=상품명
label.item.price=가격
label.item.quantity=수량

# 페이지 제목
page.items=상품 목록
page.item=상품 상세
page.addItem=상품 등록
page.updateItem=상품 수정

# 버튼
button.save=저장
button.cancel=취소​


2)각
HTML들은 다음과 같이 해당 데이터를 key 값으로 불러서 사용한다.

     addForm.html

<label for="itemName" th:text="#{item.itemName}"></label

 

국제화

메시지에서 설명한 메시지 파일( `messages.properties` )을 각 나라별로 별도로 관리하면 서비스를 국제화 할 수 있다. 예를 들어서 다음과 같이 2개의 파일을 만들어서 분류한다.

messages_en.properties
 item=Item
 item.id=Item ID
 item.itemName=Item Name
 item.price=price
 item.quantity=quantity​

messages_ko.properties

item=상품
item.id=상품 ID
item.itemName=상품명
item.price=가격
item.quantity=수량


영어를 사용하는 사람이면 `messages_en.properties` 를 사용하고, 한국어를 사용하는 사람이면 `messages_ko.properties` 를 사용하게 개발하면 된다.


한국에서 접근한 것인지 영어에서 접근한 것인지는 인식하는 방법은 HTTP `accept-language` 해더 값을 사용하거나 사용자가 직접 언어를 선택하도록 하고, 쿠키 등을 사용해서 처리하면 된다. 메시지와 국제화 기능을 직접 구현할 수도 있겠지만, 스프링은 기본적인 메시지와 국제화 기능을 모두 제공한다. 그리고 타임리프도 스프링이 제공하는 메시지와 국제화 기능을 편리하게 통합해서 제공한다.

 

스프링 메시지 소스 설정

스프링 부트

스프링 부트를 사용하면 스프링 부트가 `MessageSource` 를 자동으로 스프링 빈으로 등록한다.



스프링 부트 메시지 소스 설정
spring.messages.basename=messages,config.i18n.messages


스프링 부트 메시지 소스 기본 값
spring.messages.basename=messages


 

메시지 파일 만들기

messages.properties
hello=안녕 
hello.name=안녕 {0}

 

messages_en.properties

 hello=hello
 hello.name=hello {0}​

 

스프링 메시지 소스 사용

MessageSource 인터페이스
public interface MessageSource {
     String getMessage(String code, @Nullable Object[] args, @Nullable String
 defaultMessage, Locale locale);
     String getMessage(String code, @Nullable Object[] args, Locale locale)
throws NoSuchMessageException;

`MessageSource` 인터페이스를 보면 코드를 포함한 일부 파라미터로 메시지를 읽어오는 기능을 제공한다.


스프링이 제공하는 메시지 소스를 어떻게 사용하는지 테스트 코드를 통해서 학습하자

import static org.assertj.core.api.Assertions.*;
@SpringBootTest
public class MessageSourceTest {
    @Autowired
    MessageSource ms;
    @Test
    void helloMessage() {
        String result = ms.getMessage("hello", null, null);
	assertThat(result).isEqualTo("안녕"); }
}

 

- ms.getMessage("hello", null, null)`

 

code: `hello`
args: `null`
locale: `null`

 


가장 단순한 테스트는 메시지 코드로 `hello` 를 입력하고 나머지 값은 `null` 을 입력했다. `locale` 정보가 없으면 `basename` 에서 설정한 기본 이름 메시지 파일을 조회한다. `basename` 으로 `messages` 를 지정 했으므로 `messages.properties` 파일에서 데이터 조회한다.


MessageSourceTest 추가 - 메시지가 없는 경우, 기본 메시지
@Test
 void notFoundMessageCode() {
     assertThatThrownBy(() -> ms.getMessage("no_code", null, null))
             .isInstanceOf(NoSuchMessageException.class);
}
 @Test
 void notFoundMessageCodeDefaultMessage() {
String result = ms.getMessage("no_code", null, "기본 메시지", null);
assertThat(result).isEqualTo("기본 메시지");
}


- 메시지가 없는 경우에는 `NoSuchMessageException` 이 발생한다.
- 메시지가 없어도 기본 메시지( `defaultMessage` )를 사용하면 기본 메시지가 반환된다.


MessageSourceTest 추가 - 매개변수 사용
 @Test
 void argumentMessage() {
     String result = ms.getMessage("hello.name", new Object[]{"Spring"}, null);
assertThat(result).isEqualTo("안녕 Spring"); }​

다음 메시지의 {0} 부분은 매개변수를 전달해서 치환할 수 있다. `
hello.name=안녕 {0}  ->` Spring 단어를 매개변수로 전달 ->`안녕 Spring`



국제화 파일 선택

locale 정보를 기반으로 국제화 파일을 선택한다. Locale이 `en_US` 의경우 `messages_en_US` `messages_en` `messages` 순서로찾는다. `Locale` 에 맞추어 구체적인 것이 있으면 구체적인 것을 찾고, 없으면 디폴트를 찾는다고 이해하면 된다.
@Test
 void defaultLang() {
assertThat(ms.getMessage("hello", null, null)).isEqualTo("안녕");
assertThat(ms.getMessage("hello", null, Locale.KOREA)).isEqualTo("안녕"); }​


- ms.getMessage("hello", null, null) : locale 정보가 없으므로 `messages` 를 사용
- ms.getMessage("hello", null, Locale.KOREA)  : locale 정보가 있지만, `message_ko` 가 없으므 로 `messages` 를 사용

MessageSourceTest 추가 - 국제화 파일 선택2
 @Test
 void enLang() {
     assertThat(ms.getMessage("hello", null, Locale.ENGLISH)).isEqualTo("hello");
 }​

 

- ms.getMessage("hello", null, Locale.ENGLISH)` : locale 정보가 `Locale.ENGLISH` 이므로 `messages_en` 을 찾아서 사용



 

 

웹 애플리케이션에 메시지 적용하기

messages.properties
label.item=상품
label.item.id=상품 ID 
label.item.itemName=상품명 
label.item.price=가격 
label.item.quantity=수량

page.items=상품 목록 
page.item=상품 상세 
page.addItem=상품 등록 
page.updateItem=상품 수정

button.save=저장 
button.cancel=취소​


타임리프 메시지 적용

타임리프의 메시지 표현식 `#{...}` 를 사용하면 스프링의 메시지를 편리하게 조회할 수 있다. 예를 들어서 방금 등록한 상품이라는 이름을 조회하려면 `#{label.item}` 이라고 하면 된다.


렌더링 전

 <div th:text="#{label.item}"></h2>

 

렌더링 후

<div>상품</h2>


모든 페이지에대해 타임리프 메세지 표현식을 적용하면 "어떤 기획자가 상품명이라는 단어를 모두 상품이름으로 고쳐달라고 하면 어떻게 해야할까?" 해당 문제가 쉽게 해결된다. messages.properties에서 상품명을 상품이름으로 고치면 된다.

조금 더 실험을 해보면..

 

이렇게 맘대로 필드명을 한번에 바꿀 수 있다.

 

 

웹 애플리케이션에 국제화 적용하기

웹 애플리케이션에 국제화를 적용. 먼저 영어 메시지를 추가

messages_en.properties
 label.item=Item
 label.item.id=Item ID
 label.item.itemName=Item Name
 label.item.price=price
 label.item.quantity=quantity
 page.items=Item List
 page.item=Item Detail
 page.addItem=Item Add
 page.updateItem=Item Update
 button.save=Save
button.cancel=Cancel​

이것으로 국제화 작업은 거의 끝났다. 앞에서 템플릿 파일에는 모두 `#{...}` 를 통해서 메시지를 사용하도록 적용 해두었기 때문이다.


웹으로 확인하기
웹 브라우저의 언어 설정 값을 변경하면서 국제화 적용을 확인해보자. 크롬 브라우저 설정 언어를 검색하고, 우선 순위를 변경하면 된다. 우선순위를 영어로 변경하고 테스트해보자. 웹 브라우저의 언어 설정 값을 변경하면 요청시 `Accept-Language` 의 값이 변경된다. `Accept-Language` 는 클라이언트가 서버에 기대하는 언어 정보를 담아서 요청하는 HTTP 요청 헤더이다. (더 자세한 내용은 모든 개발자를 위한 HTTP 웹 기본지식 강의를 참고하자.)

짜잔
이렇게 메세지와 국제화를 알아보고 웹애플리케이션에서도 적용해보았다.

 

728x90