인도네시아 유저 대상 프로젝트에서 국제화를 다루다 마주친 두 가지 함정을 정리한 글이다. 표준 ISO 코드를 넣었는데 다른 값이 나가는 문제와, 숫자 포맷의 쉼표와 점이 뒤바뀌어 보이는 문제를 각각 JDK 구현까지 따라 들어가 확인한다. 결론보다 원인을 추적하는 과정이 본문의 대부분이다.
핵심 포인트- 언어는 ISO 639-1, 국가는 ISO 3166-1의 두 자리 표기를 기준으로 삼기로 팀 간에 정했다.
- 인도네시아어 코드로 id를 넣었는데 계속 in이 나가 결제 게이트웨이 쪽에서 거부됐다.
- 원인은 Locale 생성자가 하위 호환을 위해 새 코드를 구 코드로 되돌려 저장하기 때문이었다.
- he는 iw로, yi는 ji로, id는 in으로 바뀌며 이 동작은 코드에 그대로 박혀 있다.
- 인도네시아 숫자 포맷은 소수점이 쉼표이고 자리 구분이 점이라 한국 감각과 반대로 보인다.
- 그 규칙은 localedata.jar 안의 언어·국가별 리소스 파일에 정의돼 있다.
상세 정리- 상황: 회원팀과 빌링팀이 어려운 부분을 맡고 필자는 그 팀들과 통신하고 UI를 표시하는 역할이었다.
- 용어 유래: i18n은 첫 글자와 끝 글자 사이 글자 수가 18개라서 붙은 표기이며 localization도 같은 방식으로 l10n이 된다.
- 표준 합의: 애매한 것은 모두 ISO 두 자리 표기를 따르기로 도메인 담당자들끼리 정했다.
- 대상 코드: 인도네시아는 id와 ID, 대만은 zh와 TW, 태국은 th와 TH를 쓴다.
- 구현: Locale 객체를 언어와 국가로 만들어 두고 결제 게이트웨이 호출에 언어 코드를 넘겼다.
- 증상: 표준에 맞게 넣었는데 계속 표준에 맞지 않는 값을 보낸다는 답을 받았다.
- 원인 추적: Locale 생성자를 열어보니 내부에서 옛 ISO 코드로 변환하는 함수를 거치고 있었다.
- 변환 규칙: 새 코드와 옛 코드를 모두 받아들이되 하위 호환을 위해 항상 옛 코드로 저장한다는 주석이 달려 있다.
- 회피 검토: Locale을 커스터마이징하면 그것을 중요하게 쓰는 SpringMVC 쪽 코드도 크게 손봐야 해서 부담이 컸다.
- 실제 해결: 다행히 상대 팀이 어렵지 않다며 인도네시아어 코드를 in으로 받도록 바꿔줬다.
- 두 번째 문제: 숫자를 보기 좋게 만들려고 NumberFormat에 Locale을 넘겼더니 10억과 소수점 값이 점과 쉼표가 뒤바뀐 형태로 나왔다.
- 확인: 검색해보니 그 나라에서 실제로 그렇게 쓰는 표기였다.
- QA 논의: 천 단위 구분에 점을 쓰면 한국 사람에게는 1원으로 읽힐 수 있어 바꾸자는 의견이 나왔지만, 오픈 시점에는 현지 표기를 그대로 쓰기로 했다.
- 문화 차이: 현지 담당자들은 오히려 한국식 표기를 의아해했다는 후문을 덧붙이며 문화 차이로 정리한다.
- 정의 위치 추적: 오라클 Java 기준으로 jre 아래 localedata.jar에 정의돼 있고 기본 패키지가 sun.text라 초창기부터 있던 것으로 보인다.
- 리소스 구조: 언어와 국가별 FormatData 파일의 NumberElements 배열에서 0번째가 소수점, 1번째가 자리 구분자로 쓰인다.
- 마무리 실험: 그 리소스 파일을 가진 Locale들이 같은 숫자를 어떻게 표현하는지 jar를 열어 순회하는 테스트 코드를 짜본다.
왜 읽나다국어 서비스에서 표준 코드를 넣었는데 다른 값이 나가거나 숫자 표기가 예상과 다르다면, 원인이 내 코드가 아니라 플랫폼 구현에 있을 수 있다는 실제 사례다.