본문으로 건너뛰기
ElastiCORE 3.0 은 릴리스 후보(RC)입니다: Maven Central 의 3.0.261022-rc1. 2.2 라인 프로젝트는 2.2 문서를 보세요.
버전: 3.0

메시지와 언어

모든 오류 응답에는 프로그램이 분기할 수 있는 code 가 있고, 문구는 한 응답에 한 언어로 나갑니다:

  1. Accept-Language 의 언어를 순서대로 보아 애플리케이션이 번역한 언어(classpath 의 messages_<언어>, 도메인 번들 옆 <도메인>-messages_<언어>, ValidationMessages_<언어> 파일이 있는 언어), 기본 언어, 영어 중 처음 나온 것.
  2. 그런 것이 없으면(헤더가 없거나 번역이 없는 언어) 기본 언어: Spring Boot 의 spring.web.locale(Boot 3·4 같은 이름). 설정하지 않았으면 번들의 기본 문구(영어, 또는 DSL 에 적은 문장).
  3. Bean Validation 의 기본 메시지(Size, NotNull, Min ...)도 같은 언어로, 숫자를 넣어 다시 말합니다 (size must be between 0 and 10 / 크기가 0에서 10 사이여야 합니다). DSL 에 적은 문장은 적은 그대로입니다.

서버 머신의 언어는 쓰지 않습니다. 한국어 OS 의 서버도 헤더 없는 요청, en, sv 에 영어로 답합니다 (Pet Store 를 -Duser.language=ko -Duser.country=KR 로 띄워 확인).

application.yml - 헤더 없는 요청을 한국어로
spring:
web:
locale: ko

영어는 번들의 기본 문구라서, spring.web.locale: ko 여도 Accept-Language: en 이면 영어입니다. 세션·쿠키·fixed 같은 다른 LocaleResolver 를 쓰면 그 resolver 가 언어를 정합니다.

번역하는 법​

  1. 빌드하면 도메인마다 src/main/resources/i18n/<도메인>-messages.properties 가 생깁니다 - 그 도메인이 답할 수 있는 모든 코드와 기본 문구. 생성물이니 고치지 마세요.
  2. 같은 폴더에 내 파일 <도메인>-messages_ko.properties 를 두고 필요한 키만 번역합니다(UTF-8 로 저장해도 됨). 빠진 키는 기본 문구로 나갑니다.
src/main/resources/i18n/petstore-messages_ko.properties (Pet Store, 일부)
# Korean wording of petstore-messages.properties (generated beside it).
# The keys come from that file; a key missing here is said in its default wording.

error.not-found.title=찾을 수 없음
error.invalid.title=잘못된 요청
error.state.title=지금 상태에서는 할 수 없는 요청
error.not-signed-in.title=로그인이 필요합니다
error.forbidden.title=권한이 없습니다
error.unique.title=이미 있음
error.unique=이미 사용 중인 값입니다.

invariant.Item.unitCost.min=가격은 0보다 커야 합니다
invariant.PurchaseOrder.lines.minsize=장바구니가 비어 있습니다
state.PurchaseOrder.status.cancel=주문 상태가 {0}이면 취소할 수 없습니다 (가능한 상태: {1})
state.PurchaseOrder.status.ship=주문 상태가 {0}이면 배송할 수 없습니다 (가능한 상태: {1})

# A field's check (400), by Spring's message codes: <Constraint>.<field>
Size.lines=장바구니가 비어 있습니다
NotNull.lines=장바구니가 비어 있습니다

# What Spring MVC refuses before a controller runs (RequestShapeErrors reads this file too)
error.unreadable=요청 본문이 없거나 JSON 이 아닙니다.
error.method-not-allowed={0} 메서드는 여기서 쓸 수 없습니다.
typeMismatch.lines.quantity=수량은 0 이상의 정수입니다
POST /api/petstore/purchaseOrder/{id}/cancel
Accept-Language: ko

HTTP/1.1 409
{ "title": "지금 상태에서는 할 수 없는 요청",
"detail": "주문 상태가 SHIPPED이면 취소할 수 없습니다 (가능한 상태: [PLACED])",
"code": "state.PurchaseOrder.status.cancel", "status": 409 }

키​

키무엇
error.<code>.title, error.<code>공통 코드의 제목과 문장
state.<Entity>.<field>.<command>생명주기 거절 - {0} 지금 상태, {1} 가능한 상태
invariant.<Entity>.<field>.min ...필드 범위
reference.<Entity>.<relation>가리킨 행이 없음 - {0} 식별자
<제약>.<필드> (Size.lines, NotNull.customerDTO.login)필드 검증(400), Spring 의 메시지 코드: {0} 필드, 그다음 제약 속성의 이름순 (Size 는 {1} max, {2} min)
typeMismatch.<필드>타입이 받지 않는 값. typeMismatch.lines[0].quantity 는 그 줄만, typeMismatch.lines.quantity 는 모든 줄
직접 만든 코드손으로 쓴 거절에 준 코드
  • 값이 들어가는 문장(state.*, reference.*)은 ' 를 '' 로 적습니다(Java MessageFormat).
  • 필드 없는 코드(Size=, NotNull=)는 쓰지 마세요. 그 언어의 그 제약에 대한 DSL 문장을 모두 덮어 버립니다.
  • ValidationMessages*.properties 로 언어를 맞추지 마세요. 기본 파일은 Hibernate Validator 의 번역을 덮고, _<언어> 파일만 두면 다른 언어가 서버 머신의 언어로 fallback 합니다. 위의 규칙이 이미 한 응답을 한 언어로 맞춥니다.
  • 찾는 순서: classpath 의 messages_<언어>.properties, 도메인 번들과 그 옆 번역, 애플리케이션의 MessageSource, 기본 문구. 도메인 코드의 번역은 messages_<언어>.properties 나 도메인 번들 옆에 둡니다 - spring.messages.basename 으로 바꾼 번들은 도메인 번들보다 뒤에 찾습니다.

손으로 쓴 거절을 번역하기​

throw new ConflictException("loan.no-copy-left", "No copy of {0} is left to lend", isbn);
i18n/library-messages_ko.properties (내 파일) 또는 messages_ko.properties
loan.no-copy-left=빌려줄 수 있는 {0} 이(가) 없습니다

기본 문구는 코드에 적습니다 - 빌드마다 다시 쓰이는 생성 파일 <도메인>-messages.properties 에 적지 않습니다.