Home 메시지, 국제화
Post
Cancel

메시지, 국제화

메시지, 국제화?

메시지

만약 기획자가 화면에 보이는 문구가 맘에 들지 않는다고 “상품명” 이라는 단어를 모두 “상품 이름” 으로 고쳐달라고 하면 어떻게 해야할까?

각각의 화면 label에 있는 “상품명” 이라는 부분을 하나하나 다 고쳐주어야한다.

HTML 파일에 메시지가 하드코딩 되어있기 때문이다.

이런 다양한 메시지를 한 곳에서 관리하도록 하는 기능을 메시지 기능이라고 한다.

국제화

메시지를 관리하기 위해 메시지 파일이라는 것을 사용하게 되는데

이 메시지 파일을 각 나라별로 별도로 관리하면 서비스를 국제화 할 수 있다.

이렇게 하면 각 나라별로 언어에 맞게 보여줄 수 있다.

참고(어느 나라에서 접근한 것인지 인식하는 방법)

  • HTTP accept-language 헤더 값을 사용.
  • 사용자가 언어를 선택하도록 하고 쿠키 등을 사용해 처리.

메시지 관리와 국제화 기능을 직접 구현할 수 있지만 스프링은 기본적인 메시지와 국제화 기능을 모두 제공한다. 그리고 타임리프도 스프링이 제공하는 메시지와 국제화 기능을 편리하게 통합해서 제공한다.

스프링 메시지 소스 설정

메시지 관리 기능을 사용하려면 스프링이 제공하는 “MessageSource” 인터페이스를 스프링 빈으로 등록하면 된다.

인터페이스이기 때문에 구현체인 “ResourceBundleMessageSource”를 스프링 빈으로 등록한다.

1
2
3
4
5
6
7
@Bean 
public MessageSource messageSource() { 
	ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); 
	messageSource.setBasenames("messages", "errors"); 
	messageSource.setDefaultEncoding("utf-8"); 
	return messageSource; 
}
  • basenames: 설정 파일의 이름을 지정한다.
    • messages로 지정하면 messages.properties 파일을 읽어 사용한다.
    • 국제화 기능을 적용하려면 messages_en.properties, messages_ko.properties와 같이 파일명 마지막에 언어 정보를 주면 된다.
    • 국제화 파일이 없으면 messages.properties를 기본으로 사용한다.
    • 파일의 위치는 /resources/messages.properties에 두면 된다.
    • 여러 파일을 한 번에 지정할 수 있다.
      • 위의 코드에서는 “messages”와 “errors” 둘을 지정했다.
  • defaultEncoding : 인코딩 정보를 지정한다.

하지만 스프링 부트를 사용하면 스프링 부트가 MessageSource를 자동으로 스프링 빈으로 등록해준다.

스프링 부트 메시지 소스 설정

스프링 부트를 사용하면 application.properties에 아래와 같이 메시지 소스를 설정한다.

1
spring.messages.basename=messages,config.i18n.messages

콤마(,) 뒤는 경로를 나타낸다.

  • /resources/messages.properties (기본)
  • /resources/config/i18n/messages.properties
1
2
// 기본값
spring.messages.basename=messages

메시지 파일 만들기

/resources에 messages.properties와 meesages_en.properties를 만든다.

1
2
3
4
5
hello=안녕
hello.name=안녕 {0}

hello=hello
hello.name=helo {0}

그리고 각각 위의 내용을 넣는다.

스프링 메시지 소스 사용

test로 해본다.

1
2
3
4
5
6
7
8
9
10
11
@SpringBootTest
public class MessageSourceTest{
	@Autowired
	MessageSource ms;
	
	@Test
	void helloMessage(){
		String result = ms.getMessage("hello", null, null);
		assertThat(result).isEqualTo("안녕");
	}
}
  • code : “hello”
  • args : null
  • locale : null

메시지가 없는 경우

1
2
3
4
5
6
@Test  
void notFoundMessageCode() {  
	assertThatThrownBy(() -> messageSource.getMessage("no_code", null, null))  
			.isInstanceOf(NoSuchMessageException.class);  
}

예외를 발생시키지 않고 defaultMessage를 인자로 줄 수도 있다.

1
ms.getMessage("no_code", null, "기본 메시지", null);

매개변수 사용

1
2
@Test
ms.getMessage("hello.name", new Object[]{"Spring"}, null);

messages.properties의 hello.name 의 메시지의 중괄호 0번 부분에 “Spring”이라는 단어를 매개변수로 전달한 것이다.

국제화 파일 선택

  • local 정보를 기반으로 국제화 파일을 선택한다.
  • en_US의 경우 messages_en_US -> messages_en -> messages 순서로 찾게된다.
  • locale에 맞추어 구체적인 것이 있으면 구체적인 것을 찾고 없으면 디폴트를 찾는다.
1
ms.getMessage("hello", null, Locale.English);

참고

나는 처음에 테스트에 실패했다. 기대값이 “안녕”이 아닌 “??”로 나왔기 때문이다.

이유는 IntelliJ 설정에서 properites 파일의 Encoding 값이 UTF-8로 안되어있어서 한글이 깨졌기 때문이다.

설정 후 -> gradle build-clean -> build-build 하고 나니 해결되었다.

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

Test로는 적용해보았고 실제 웹 애플리케이션에 어떻게 적용하는지 확인해본다.

메시지를 아래와 같이 추가한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
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=취소

타임리프 메시지 적용

1
#{...}

를 사용하면 스프링의 메시지를 편리하게 조회할 수 있다.

위에서 등록한 상품이라는 이름을 조회하려면

1
#{label.item}

을 이용하면 된다.

기존의 코드에는 th:text를 사용하지 않고 그냥 “상품 등록 폼”을 사용했다.

위와 같이 바꾸면 messages.properties에 해놓은 page.addItem이 우선시되어 “상품 등록 폼” -> “상품 등록” 으로 바뀌게 된다.

참고(파라미터 사용)

1
<p th:text="#{hello.name(${item.itemName})}"></p>

위와 같이 사용할 수 있다.

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

위에서 다 th:text=#을 통해 다 바꾸어 주었기 때문에 언어의 우선순위만 변경하면 된다.

크롬 브라우저 -> 설정 -> 언어를 검색하고 우선순위를 변경하면 된다.

웹 브라우저의 언어 설정 값을 변경하면 요청 시 Accept-Language 값이 변경된다.

스프링의 국제화 메시지 선택

MessageSource의 메시지 기능은 Locale을 알아야 언어를 선택할 수 있다.

결국은 스프링도 Locale 정보를 알아야 언어를 선택할 수 있는데, 스프링은 언어 선택 시 기본으로 Accept-Language 헤더의 값을 사용한다.

LocalResolver

스프링은 Locale 선택 방식을 변경할 수 있도록 LocaleResolver라는 인터페이스를 제공한다.

스프링부트는 기본적으로 Accept-Language를 활용하는 AcceptHeaderLocaleResolver를 사용한다.

LocalResolver 인터페이스

1
2
3
4
public interface LocaleResolver { 
	Locale resolveLocale(HttpServletRequest request); 
	void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale); 
}

만약 Locale 선택 방식을 변경하려면 LocaleResolver의 구현체를 변경해서 쿠키나 세션 기반의 Locale 선택 기능을 사용할 수 있다.

이와 관련해서는 LocaleResolver를 검색하면 수 많은 예제가 나온다. 필요할 때 참고하도록 한다.

정리

메시지, 국제화 기능을 사용하면 화면에 보이는 문구에 대한 관리가 매우 쉬워진다.

서비스가 작을 땐 상관없지만 커졌을 때 수정해야 한다면 하나하나 수정할 필요없이 messages.properties 같은 메시지 파일만 관리해주면 된다.

This post is licensed under CC BY 4.0 by the author.