DESC

내가 보려고 쓰는 블로그

«   2025/06   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Today
-
Yesterday
-
Total
-
  • Spring 다국어 적용하기
    JAVA 2020. 5. 27. 15:04
    반응형

    특정 사이트에서 다국어 서비스를 구현하기 위해 Message Source 를 활용하여 다국어를 적용하는 소스를 작성해보려고 한다.

    화면단에서는 /common/messages.js 를 호출하여 마치 js 파일을 사용하는 듯 한 모습을 하지만 이는 Request Mapping을 통하여 서버에서 properties 파일을 읽어들이는 식으로 구성하고자 한다.

     

    - Controller  com/hailey/message/MessageController.java

    package com.hailey.message.controller;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.i18n.LocaleContextHolder;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.servlet.ModelAndView;
    
    import com.hailey.message.service.MessageResolveService;
    
    @Controller
    public class MessageController {
    
      @Autowired
      private MessageResolveService messageResolveService;
      
      @RequestMapping(value="/common/messages.js")
      public ModelAndView strings(HttpServletRequest request, HttpServletResponse response) {
        ModelAndView modelAndView = new ModelAndView("message/messages", "keys", messageResolveService.getMessages(LocaleContextHolder.getLocale()));
        modelAndView.addObject("content-type", "text/javascript");
        
        String acceptEncoding = request.getHeader("Accept-Encoding");
        if(acceptEncoding != null && acceptEncoding.indexOf("gzip") != -1) {
          response.addHeader("Content-Encoding", "gzip");
        }
        
        return modelAndView;
      }
    }
    

     

    - Bean  com/hailey/message/ExposedResourceBundleMessageSource.java 메시지 소스를 읽어오기 위함

    package com.hailey.message;
    
    import java.io.IOException;
    import java.util.Locale;
    import java.util.Properties;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.context.support.ReloadableResourceBundleMessageSource;
    import org.springframework.core.io.Resource;
    
    
    public class ExposedResourceBundleMessageSource extends ReloadableResourceBundleMessageSource {
    
      protected final Logger logger = LoggerFactory.getLogger(this.getClass());
    
      @Override
      protected Properties loadProperties(Resource resource, String filename) throws IOException {
        logger.info("Load " + filename);
        return super.loadProperties(resource, filename);
      }
    
      /**
       * Gets all messages for presented Locale.
       * 
       * @param locale user request's locale
       * @return all messages
       */
      public Properties getMessages(Locale locale) {
        return getMergedProperties(locale).getProperties();
      }
    }

     

    - Service ( interface 클래스를 상속받으나 생략함 )   com/hailey/message/service/MessageResolveServiceImpl.java 

    package com.hailey.message.service;
    
    import java.util.HashMap;
    import java.util.Locale;
    import java.util.Map;
    import java.util.Properties;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.context.MessageSource;
    import org.springframework.context.NoSuchMessageException;
    import org.springframework.stereotype.Service;
    import org.springframework.web.context.support.XmlWebApplicationContext;
    
    import com.hailey.message.ExposedResourceBundleMessageSource;
    
    @Service
    public class MessageResolveServiceImpl implements MessageResolveService {
    
      protected final Logger logger = LoggerFactory.getLogger(this.getClass());
    
      private MessageSource messageSource;
    
      @Override
      public String getMessage(String key, Object[] arguments, Locale locale) {
        String message = "";
        try {
          message = messageSource.getMessage(key, arguments, locale);
        } catch (NoSuchMessageException e) {
          message = key;
          logger.warn("No message found: " + key);
        }
        return message;
      }
    
      @Override
      public Map<String, String> getMessages(Locale locale) {
        Properties properties = ((XmlWebApplicationContext) messageSource).getBean("messageSource", ExposedResourceBundleMessageSource.class).getMessages(locale);
        Map<String, String> messagesMap = new HashMap<String, String>();
        for (Map.Entry<Object, Object> entry : properties.entrySet()) {
          if (entry.getKey() != null && entry.getValue() != null) {
            messagesMap.put(entry.getKey().toString(), entry.getValue().toString());
          }
        }
        return messagesMap;
      }
    
      @Override
      public void setMessageSource(MessageSource messageSource) {
        logger.info("Messages i18n injected");
        this.messageSource = messageSource;
      }
    
    }

     

    - View (js 파일)  /WEB-INF/view/message/messages.jsp

    <%@page language="java" contentType="text/javascript; charset=utf-8" pageEncoding="utf-8" %>
    <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
    <% pageContext.setAttribute("newLineChar", "\n"); %>
    var messages = new Array();
    <c:forEach var="map" items="${keys}">
    messages["${map.key}"] = "${fn:replace(map.value, newLineChar,'\\n')}";
    </c:forEach>
    // Sample
    var messages = new Array();
    messages["LABEL.JOIN"] = "회원가입";
    messages["MSG.LOGIN.PWD"] = "비밀번호";
    messages["MSG.ERROR.PWD_VALID"] = "비밀번호는 영문 + 숫자 + 특수문자 혼합 8자리 이상이어야 합니다.";

     

    - 메시지 정의 파일  /WEB-INF/message/messages.properties (Default)
    각 언어 별로 파일명을 messages_en.properties , messages_kr.properties 등으로 구분한다.
    Eclipse 툴을 이용하면 메시지 파일을 간단히 작성할 수 있고, KEY 값을 . 기호로 트리구조로 작성할 수 있다.

    #messages_en.properties
    #Generated by Eclipse Messages Editor (Eclipse Babel)
    
    LABEL.JOIN                      = Join
    MSG.LOGIN.PWD                   = Password
    MSG.ERROR.PWD_VALID             = Your password must be a \
                                    combination of alphabetic, \
                                    numeric and special characters \
                                    of at least 8 characters.
    #messages_kr.properties
    #Generated by Eclipse Messages Editor (Eclipse Babel)
    # 이클립스 GUI에서 한글을 입력하면 아래와 같이 유니코드 문자로 변환하여 저장된다.
    
    LABEL.JOIN                      = \uD68C\uC6D0\uAC00\uC785
    MSG.LOGIN.PWD                   = \uBE44\uBC00\uBC88\uD638
    MSG.ERROR.PWD_VALID             = \uBE44\uBC00\uBC88\uD638\uB294 \
                                    \uC601\uBB38 + \uC22B\uC790 + \
                                    \uD2B9\uC218\uBB38\uC790 \uD63C\
                                    \uD569 8\uC790\uB9AC \
                                    \uC774\uC0C1\uC774\uC5B4\uC57C \
                                    \uD569\uB2C8\uB2E4.

     

    - Spring Context 에 지정하기  /WEB-INF/spring/context-message.xml 

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    
      <bean id="messageSource" class="com.hailey.message.ExposedResourceBundleMessageSource">
        <property name="basenames">
          <list>
            <!-- 메세지 파일의 위치를 지정합니다. message_언어.properties 파일을 찾습니다. -->
            <value>/WEB-INF/messages/message</value>
          </list>
        </property>
        <!-- 파일의 기본 인코딩을 지정합니다. -->
        <property name="defaultEncoding" value="UTF-8" /> <!-- properties 파일이 변경되었는지 확인하는 주기를 지정합니다. 60초 간격으로 지정했습니다. -->
        <property name="cacheSeconds" value="60" />
      </bean>
      
      <!-- 언어 정보를 세션에 저장하여 사용합니다. -->  
      <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
        <property name="cookieName" value="LOCALE" />
        <property name="cookieMaxAge" value="2592000" />
      </bean>
    </beans>

     

    - 클라이언트  javascript 활용 예

    화면에서 언어 설정은 쿠키를 사용하는데 위에 작성한 context-message.xml 에 명시한 cookieName 을 사용해야 한다.

    // 창이 로드될 때 메시지 js 파일을 호출하여 캐시해놓고 사용한다.
    window.onload = function(){
      jQuery.ajax({
          dataType: 'script',
          callType: 'skip_success',
          cache: true,
          url: '/common/messages.js'  /* Controller 에서 지정한 url */
        });
    
    };
    
    
    // 메시지 읽어들일 때 아래와같이 함수를 정의하였다.
    function getMessage(key, default_value) {
      if(typeof messages == 'undefined') {
        return default_value || '';
      }
      return nvl(messages[key]);
    }
    
    
    // 언어 변경 시 쿠키 값에 세팅해야 한다. 그래야 페이지 새로고침 시 message.js 파일에 영향이 있음.
    // context 에 지정한 60초 이내에는 바로 적용되지 않을 수 있음.
    function changeLocale(locale) {
      document.cookie = "LOCALE="+nvl(locale, 'kr');
    }
    
    
    console.log( getMessage("LABEL.JOIN") );  // 회원가입

     

    반응형

    댓글

Customed By Hailey Gong.