스프링 레시피 CH2.7 프로퍼티 파일에서 로케일마다 다른 다국어 메시지를 해석하기

2023. 12. 14. 00:01spring

과제

애너테이션을 이용해 다국어를 지원하는 애플리케이션을 작성하세요.

 

해결책

MessageSource 인터페이스에는 리소스 번들 메시지를 처리하는 메서드가 몇 가지 정의되어 있다.

ResourceBundleMessageSource는 가장 많이 쓰이는 MessageSource 구현체로, 로케일별로 분리된 리소스 번들 메시지를 해석한다.ResourceBundleMessageSource POJO를 구현하고 자바 구성 파일에서 @Bean을 붙여 선언하면 애플리케이션에서 필요한 i18n 데이터를 가져다 쓸 수 있다.

 

풀이

미국(로케일)의 영어(언어)에 해당하는 message_en_US.properties 리소스 번들을 예로 들겠다. 리소스 번들은 클래스패스 루트에서 읽으므로 이 경로에 파일이 있는지 확인하고 다음과 같이 키-값을 기재한다.

alert.checkout=A shopping cart has been checked out.
alert.inventory.checkout=A shopping cart with {0} has been checked out at {1}

 

리소스 번들 메시지를 구분 처리하려면 ReloadableResourceBundleMessageSource 빈 인스턴스를 자바 구성 파일에 정의한다.

 

@Configuration
@PropertySource("classpath:discounts.properties")
@ComponentScan("com.spring.study.chapter02.shop")
public class ShopConfiguration {

    @Value("classpath:banner.txt")
    private Resource banner;
    
    @Bean
    public ReloadableResourceBundleMessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource =
                new ReloadableResourceBundleMessageSource();
                
        //자바 클래스패스에서 이름이 messages로 시작하는 파일들을 찾도록 설정
        messageSource.setBasename("classpath:messages");
        
        //캐시 주기는 1초로 설정해서 쓸모없는 메시지를 다시 읽지 않는다.
        messageSource.setCacheSeconds(1);
        return messageSource;
    }
.
.
.
}

 

빈 인스턴스는 반드시 messageSource라고 명명해야 애플리케이션 컨텍스트가 알아서 감지한다.

빈 정의부에서 setBasenames() 메서드에 가변 문자열 인수를 넘겨 ResourceBundleMessageSource 번들 위치를 지정한다.

 

 

이렇게 MessageSource를 정의하고 영어가 주 언어인 미국 로케일에서 텍스트 메시지를 찾으면 message_en_US.properties 리소스 번들 파일이 제일 먼저 잡힌다.

 

만약 이런 이름을 가진 파일이 클래스패스에 없거나 해당 메시지를 찾지 못하면 언어(영어)에 맞는 mesages_en.properties 파일을 잡고 이 파일마저 없으면 전체 로케일의 기본 파일 messages.properties를 선택한다.

 

애플리케이션 컨텍스트를 구성해서 getMessage() 메서드로 메시지를 해석할 수 있다.

 

첫 번째 인수 : 메시지 키

두 번째 인수 : 메시지 매개변수 배열

세 번째 인수 : 대상 로케일

public class MessageExample {

    public static void main(String[] args) {
        ApplicationContext context =
                new AnnotationConfigApplicationContext(ShopConfiguration.class);

        String alert = context.getMessage("alert.checkout", null, Locale.US);
        String alert_inventory = context.getMessage("alert.inventory.checkout",
                new Object[] {"[DVD-RW 3.0]", new Date()}, Locale.US);

        System.out.println("The I18N message for alert.checkout is: " + alert);
        System.out.println("The I18N message for alert.inventory.checkout is: " +
                alert_inventory);
    }
}
=>
The I18N message for alert.checkout is: A shopping cart has been checked out.
The I18N message for alert.inventory.checkout is: A shopping cart with [DVD-RW 3.0] has been checked out at 12/13/23, 11:52 PM

 

MessageExample 클래스는 애플리케이션 컨텍스트를 직접 가져올 수 있으므로 텍스트 메시지를 해석할 수 있지만 텍스트 메시지를 해석하는 빈에는 MessageSource 구현체를 넣어야 한다.

@Component
public class Cashier {
    
    @Autowired
    private MessageSource messageSource;

    public void setMessageSource(MessageSource messageSource) {
        this.messageSource = messageSource;
    }
    
    public void checkout(ShoppingCart cart) {
        String alert = messageSource.getMessage("alert.inventory.checkout",
                new Object[]{cart.getItems(), new Date()}, Locale.US);
        
        System.out.println(alert);
    }
}