웹 개발/Back End

백엔드 다국어 처리

L3m0n S0ju 2025. 5. 31. 11:08

 

 

 

 

스프링에서 다국어 처리하는 법을 다뤄보겠습니다.

 

 

 

 

MessageSourceConfig.java

@Configuration
public class MessageSourceConfig {

    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasenames("classpath:messages/messages_common",
            "classpath:messages/messages_core",
            "classpath:messages/messages",
            "classpath:messages/messages_notice",
            "classpath:messages/messages_alarm");

        messageSource.setDefaultEncoding("UTF-8");
//        messageSource.setCacheSeconds(60); // 리로드 주기 설정 (초 단위, 0은 캐시 사용 안 함)
        return messageSource;
    }

}
  • 최상위 모듈에 messageSource 함수를 @Bean으로 등록합니다.

 

 

 

 

########################################
공통(0 ~ 1999),
########################################
API-0000=입력값을 확인해주세요.
API-0001=로그인 정보가 없습니다.
API-0002=필수 입력값입니다.
API-0003=유효하지 않은 회사입니다.
API-0004=권한이 없습니다.
API-0005=전자결재를 상신 하려는 회사와 사용자의 소속 회사가 일치하지 않습니다.
API-0006=전자결재 드래프트를 그룹웨어에 전달 하였지만 실패 응답을 받았습니다.
API-0007=유효하지 않은 사용자입니다.

########################################
휴가 현황 message,
########################################
leave.other=외
leave.item=건
leave.granted.upon.request=신청 시 지급
leave.granted.upon.years.of.service={0}년 근속 시 지급
leave.every.year=매년 {0} 지급
leave.every.month=매월 {0}일 지급
leave.every.week=매주 {0} 지급
leave.direct.grant=직접 지급
leave.day=일
leave.half.day=반차
leave.quarter.day=반반차
leave.hour={0}시간
leave.minute={0}분
  • 파일이름이 messages 인 경우 messages.properties는 기본 파일이고 messages_ko.properties, message_en.properties 등 파일을 만듭니다. 예외는 코드를 등록하고 일반 문장은 키 값을 넣어줍니다.

 

 

 

MessageCreator.java (일반적인 다국어 처리된 값 가져오기)

@Component
@RequiredArgsConstructor
public class MessageCreator {

    private final MessageSource messageSource;

    public final String getMessage(String key, String... params) {
        return messageSource.getMessage(key, params, LocaleContextHolder.getLocale());
    }

}
  • 프론트에 GET /api/welcome?lang=ja 이런식으로 언어 정보를 넘겨주면 LocaleContextHolder.getLocale()을 통해서 언어 정보를 가져올 수 있다고 합니다. 따라서 메시지를 번역할 때는 위 코드처럼 messageCreator를 만들어서 사용하거나 바로 사용하거나 하면 됩니다.

 

 

 

 

파라미터가 있는 다국어 처리된 값 가져오기

@Component
@RequiredArgsConstructor
public class MessageUtils {

    private final MessageSource messageSource;

    public final String getMessageWithParams(String key, String... params) {
        return messageSource.getMessage(key, params, LocaleContextHolder.getLocale());
    }

    public final String resolveLeaveDescription(String message) {
        List<String> messageList = List.of(message.split(":"));

        if (messageList.isEmpty()) {
            return "";
        }
        String key = messageList.get(0);
        String[] params = messageList.subList(1, messageList.size()).toArray(new String[0]);

        return getMessageWithParams(key, params);
    }

}
  • DB에는 leave.every.month:7 이런식으로 저장되어 있으므로 가져와서 :를 기준으로 key와 params를 분리합니다. 그리고 마찬가지로 messageSource의 getMessage 함수를 사용하면 파라미터가 적용된 다국어 처리된 값을 가져올 수 있습니다. 

 

 

 

StatusDescriptionService.java (DB에 값 저장하기)

@Slf4j
@Service
@RequiredArgsConstructor
public class StatusDescriptionService {

    private final MessageUtils messageUtils;

    public String getAnnualLeaveDescription(int yearsOfService) {
        return ColonMessageBuilder.of("leave.annual.grant.year", yearsOfService);
    }
    
    public String getCustomLeaveRegularSchedulingYearly(LocalDate now) {
        return ColonMessageBuilder.of("leave.custom.regular.scheduling.yearly", getMmDdString(now));
    }

    public String getCustomLeaveRegularSchedulingServiceYears(int year) {
        return ColonMessageBuilder.of("leave.custom.regular.scheduling.service.years", year);
    }

    public String getExcelUpload(String monthDay) {
        return ColonMessageBuilder.of("leave.annual.excel.upload", monthDay);
    }
}
  • DB에 값을 저장할 때는 아래와 같이 key와 파라미터에 포맷을 지정 후 저장합니다.

 

ColonMessageBuilder.java

public class ColonMessageBuilder {
    public static String of(String key, Object... params) {
        return Stream.concat(Stream.of(key), Arrays.stream(params))
                .map(String::valueOf)
                .collect(Collectors.joining(":"));
    }
}

 

하지만 문제점이 있습니다. 파라미터 값은 꺼낼 때 다국어 처리가 안된다는 점입니다. 그래서 저장할 때 다국어 처리를 미리해서 넣어줘야하는데 이렇게 되면 해외에서 사이트에 들어가면 파라미터 부분은 DB에 저장할 당시의 값이 보이게 됩니다.

 

 

 

 

public String getTranslatedMessage(String colonString) {
    String[] parts = colonString.split(":");
    String key = parts[0];
    Object[] params = Arrays.stream(parts)
        .skip(1)
        .map(param -> {
            // 메시지 키처럼 생겼다면 번역 시도 (예: dayOfWeek.MONDAY)
            if (param.matches("[a-zA-Z0-9_.]+")) {
                try {
                    return messageSource.getMessage(param, null, LocaleContextHolder.getLocale());
                } catch (NoSuchMessageException e) {
                    return param; // 메시지가 없으면 원본 사용
                }
            }
            return param;
        })
        .toArray();
    
    return messageSource.getMessage(key, params, LocaleContextHolder.getLocale());
}

만약에 파라미터 또한 무조건 다국어 처리를 해야한다면 위 코드처럼 하는 방법이 있다고 GPT가 알려주었습니다.