다양한 사용자의 언어 및 지역에 맞춤 서비스를 제공하는 것을 국제화라고 한다. 영어로는 Internationalization인데 줄여서 i18n이라고도 한다. i, n 사이에 18자가 생략되었다는 의미이다.
국제화를 위해서는 특정 지역과 언어별로 "로케일"을 구성하여 그에 맞에 소프트웨어를 구성하는 지역화(Localization, l10n)가 필요하다. Java는 로케일을 나태낼 수 있도록 java.util.Locale
객체를 제공한다.
로케일은 언어와 지리적 지역을 식별한다. 로케일에 따라 사용자에게 데이터를 표시하는 방법이 달라진다. 기본적인 언어, 대소문자 표시, 데이터 정렬, 날짜 및 시간 형식, 숫자 및 통화 형식에 영향을 준다.
java.text.NumberFormat
, java.text.DateFormat
와 같이 locale-sensitive한 클래스는 로케일 정보에 따라 숫자나 날짜의 형식을 지역화하여 표시해준다.
자주 사용되는 로케일은 static
객체로 이미 만들어져 있다. (권장 X)
public final class Locale implements Cloneable, Serializable {
public static final Locale KOREAN = createConstant("ko", "");
public static final Locale KOREA = createConstant("ko", "KR");
}
Locale.KOREAN
, Locale.KOREA
Locale.ENGLISH
, Locale.US
, Locale.UK
그러나 아무래도 만들어져있는 로케일은 몇 종류 없다. 로케일 상수가 없는 새로운 로케일을 추가한다면 코드의 일관성이 깨지게 된다. 또한 locale-sensitve한 클래스들이 로케일 상수가 존재하는 로케일에만 지역화를 지원하는 것도 아니다. 예를 들어 남미 로케일 상수는 존재하지 않지만 locale-sensitve한 클래스들이 지역화를 아주 잘 지원한다.
이러한 이유로 로케일은 그냥 생성자로 만드는 것이 좋다고 한다.
Locale
은 세 가지 생성자를 갖고 있다.
Locale(String language)
Locale(String language, String country)
Locale(String language, String country, String variant)
//일반적인 영어를 사용하는 로케일 생성
Locale locale1 = new Locale("en");
//영어 사용, 캐나다 지역의 로케일 생성
Locale locale2 = new Locale("en", "CA");
//영어 사용, 미국 지역, 실리콘밸리의 로케일 생성
Locale locale3 = new Locale("en", "US", "SiliconValley");
생성자 파라미터의 language
는 소문자, country
는 대문자이다.
language
: ISO 640의 alpha-2(알파벳 2자) 또는 alpha-3(알파벳 3자) 또는 8자 이하의 언어 하위태그를 사용한다.country
: ISO 3166 alpha-2 국가 코드 또는 UN M.49 numeric-3 지역 코드를 사용한다.variant
: 로케일을 세분화하기 위해 지원하는데 자주 사용되지 않는 것 같다.참고) new Locale(”hello”)
처럼 아무 값이나 넣어도 컴파일 오류가 나지는 않는다. Locale
은 아무 것이나 만들 수는 있지만 사용하는 라이브러리 등에서 지원하는가의 문제이다.
메시징(Messaging)이란 국제화를 위한 핵심 기능이다. 표시하고자 하는 문자열을 단순하게 리터럴로 표시하지 않고, 별도의 메세지 관리 파일에서 키를 통해 값을 찾아와 표시하는 방법이다.
메시지 관리 파일을 언어 별로 만들면, 동일한 키를 가지고 다양한 언어의 값을 가져올 수 있다. 예를 들어 “greeting”이라는 키 하나만 있으면 사용자의 로케일에 맞게 “안녕하세요?”, “Hi.”, “こんにちは。” 등으로 맞춤 문자열을 가져올 수 있다.
메시지 소스 설정하기: 스프링 부트를 사용한다면 바로 사용할 수 있다. 그렇지 않다면 MessageSource
빈을 등록해주어야 한다.
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasenames("messages", "errors");
messageSource.setDefaultEncoding("utf-8");
return messageSource;
}
메시지 관리 파일 만들기: resources 바로 밑에 messages.properties를 만들어준다.
basename
)은 messages인데 설정을 통해 변경 가능하다.basename
바로 뒤에 언더바와 함께 로케일 정보를 붙여준다. messages_en.properties, messages_ko_KR.properties와 같은 형식이다.메시지 불러오기:
MessageSource
인터페이스의 getMessage()
메서드를 통해 불러올 수 있다.String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;
th:text=”#{key}"
문법을 통해 메시지를 표시할 수 있다.메시지, 국제화 기능을 위한 핵심 인터페이스는 MessageSource
와 LocaleResolver
이다.
스프링은 기본적으로 클라이언트의 Locale
정보를 기준으로 해당하는 MessageSource
를 찾는다. 즉 Locale
값을 변경함으로써 다국어 지원을 할 수 있는 것이다.
그렇다면 Locale
정보는 어떻게 정해지는 것일까? 기본적으로는 HTTP Accept-Language
헤더를 참고하여 정해진다. 그리고 이 역할을 하는 것이 LocaleResolver
이다. (참고로 이 헤더의 값은 클라이언트의 브라우저, OS 등 환경에 따라 달라진다.)
로케일 선택 전략을 바꾸고 싶다면 LocaleResolver
인터페이스의 구현체를 변경하면 된다.
AcceptHeaderLocaleResolver
Accept-Language
헤더를 참고하여 로케일을 설정한다. 로케일을 지정하는 setLocale()
메서드는 없으며 클라이언트의 설정 변경을 통해서만 로케일 변경이 가능하다.LocaleResolver
의 기본 구현체이다.FixedLocaleResolver
setLocale()
메서드는 없다.SessionLocaleResolver
Accept-Language
헤더 정보, 서버의 로케일 정보를 사용한다. 애플리케이션에 HttpSession
이 사용되는 경우 가장 적합하다(로케일 저장만을 위한 세션 생성이 아닌 경우).HttpServletRequest
에 대한 HttpSession
을 관리한다.CookieLocaleResolver
Accept-Language
헤더 정보, 서버의 로케일 정보를 사용한다. 사용자 세션을 사용하지 않는 Stateless 애플리케이션에 특히 유용하다.