글로벌 서비스 개발하는 것은 생각보다 복잡하다.
단순히 번역된 텍스트만 교체뿐만 아니라 언어마다 다른 규칙들, 레이아웃 문제들, 운영 이슈 등이 존재한다.
18개 언어를 지원하면서 겪었던 경험들을 정리해보았다.
I18n
i18n(internationalization의 약어, i + 18글자 + n)은 문화, 지역, 언어가 다양한 대상 고객을 위해 쉽게 현지화할 수 있는 제품, 애플리케이션 또는 문서 콘텐츠를 설계하고 개발하는 것을 말한다.
기본적으로 어떠한 라이브러리 없이 그냥 글로벌 개발을 한다고 생각해보면 끔찍하다. 미국과 한국만 비교해도 화폐단위, 날짜표기법 등이 다르다. 모든 국가를 고려해서 파싱하는 것은 끔찍할 것이다.
다행히도 이것을 실제로 구현해놓은 ICU라는 오픈소스가 존재한다.
ICU
- *ICU(International Components for Unicode)**는 i18n 개념을 실제로 구현한 C/C++ 기반의 오픈소스 라이브러리로 모든 주요 브라우저 엔진에서 사용중이다.
- 날짜/시간 포맷 (DateTimeFormat)
- 숫자 포맷 (NumberFormat)
- 통화
- 복수형 규칙 (PluralRules)
- 문자열 정렬 (Collator)
- TimeZone, Calendar
- Unicode normalization
국가마다 다른 모든 것을 ICU에 위임하여 사용하게 된다.
1) Intl
브라우저나 Node.js 엔진에서는 Intl API들을 통해 ICU를 호출하여 사용하게 된다.
언어를 제외하고는 기본적으로 라이브러리 설치 없이 엔진에서 제공되는 Intl API를 통해서 국제화 처리가 가능하다.
- date 예시
const date = new Date("2025-03-01T15:30:00Z");
const ko = new Intl.DateTimeFormat("ko-KR", {
dateStyle: "full",
timeStyle: "short",
timeZone: "Asia/Seoul",
});
const us = new Intl.DateTimeFormat("en-US", {
dateStyle: "full",
timeStyle: "short",
timeZone: "America/New_York",
});
console.log(ko.format(date));
// 2025년 3월 2일 일요일 오후 12:30
console.log(us.format(date));
// Saturday, March 1, 2025 at 10:30 AM
그 외에 언어를 관리하기 위해서도 ICU Message Format에 맞춰진 번역된 언어를 사용해야 한다.
2) ICU Message Format
언어마다 복수형 규칙이 다르다. 영어는 1개/2개 이상으로 구분되지만, 러시아어는 1/2-4/5 이상/11-14 등 4가지 형태가 있다.
이를 if문으로 처리하려면 각 언어의 복수형 규칙을 모두 코드로 구현해야 한다. ICU Message Format은 이 문제를 해결하기 위한 표준 문법이다.
- 복수형 처리
// 한국어
`{count, plural, other {# 개의 상품}}` // 영어
`{count, plural,
one {# item}
other {# items}}` // 러시아어
`{count, plural,
one {# книга}
few {# книги}
many {# книг}
other {# книги}}`;
번역가들도 표준화된 문법에 따라 직접 복수형 규칙을 작성할 수 있다. Crowdin, Phrase 같은 번역 플랫폼도 ICU 문법을 지원한다.
- json
{
"items": "{count, plural, other {# 개의 상품}}"
}
JSON 파일만 전달하면 번역가가 각 언어에 맞게 one, few, many 등을 채워서 반환한다. 개발자는 복수형 로직을 건드리지 않아도 된다.
그러나 Intl API에서는 message format을 제공하지 않으니 react-intl이나 next-intl 같은 ICU 규칙을 따르는 라이브러리를 상황에 따라 선택해서 포매팅하면 된다.
텍스트 줄바꿈
언어는 기본적으로 CJK 한중일 문자와 한중일 문자가 아닌 Non-CJK 문자로 구분된다.
| 분류 | 기본 중단점 | 특징 |
|---|---|---|
| Non-CJK (영어 등) | 공백(Space) | 단어 경계에서만 줄바꿈, 단어 중간 분리 불가 |
| CJK (한중일) | 음절/문자 | 어떤 문자 사이에서도 줄바꿈 가능 |
Non-CJK는 기본적으로 공백이 break point가 되어서 자동 줄바꿈이 발생하게 된다.
CJK는 기본적으로 모든 문자가 break point가 되어서 자동 줄바꿈이 되지만 문화적으로는 사실 약간 다르다.
1) CJK
- 한국어
기본값인 모든 단어가 잘리게 되면, 음절 단위로 문자가 구분되게 된다. 한국어 같은 경우는 음절 단위로 잘려도 가독성에 크게 무리가 없긴 하지만, 실무에서 UX적으로는 단어 단위로 잘리기를 원하는 요구사항이 잦다.
이런 경우에는 한국어에만 word-break 속성을 keep-all로 설정함으로써 대응할 수 있었다.
- 일본어
일본어의 단어들은 여러 개의 문자가 결합해서 하나의 음절이 되는 형태의 언어이다.
하지만 일본어에는 공백(break point)이 없기 때문에 영어 글자들이 전부 잘리는 것과 비슷하게 느껴질 수 있다.
그렇기에 일본어는 line-break CSS를 통해 국가 언어 규칙에 맞는 줄바꿈을 적용시켜야 한다.
2) Non-CJK
Non-CJK의 경우는 break point인 공백이 컨텐츠 영역 안에 들어오지 않는다면 text가 overflow 되는 상황이 발생할 수 있다.
예를 들면, 너무 긴 단어나 url이 대표적이다.
이럴 때는 overflow-wrap(word-wrap) CSS를 활용해서 처리가 필요하다.
정리
정리해보면 국가별로 텍스트의 줄넘김은 다르기 때문에 overflow-wrap, word-break, line-break CSS들을 적절히 이용해서 대응해야 한다.
쉽지 않은 QA
디자이너, 개발자 둘 다 마찬가지로 실제 개발 단계에서는 익숙한 언어로 개발하게 된다. 보통은 영어나 한국어가 될 텐데, 다른 언어로 QA 해볼 때는 많은 문제가 발생할 수 있다.
1) 긴 언어
작은 버튼에 단순한 맥락의 텍스트였지만 러시아어에서는 아주 긴 텍스트가 나오면서 레이아웃이 예상한 것과 다르게 노출되는 현상은 아주 많이 보게 된다. 그렇기 때문에 QA 때 레이아웃 자체가 변경되는 경우도 잦을 수 있다.
2) 폰트 높이
언어별로 text 길이와 폰트 높이가 전부 다르기 때문에 고정된 값을 사용할 때는 항상 주의가 필요하다.
컨텐츠 높이를 고정하고 padding을 통해 상하 높이를 조정하는 경우에는 언어마다 폰트 자체가 잘리는 현상을 마주할 수 있다.
flex 기반이나 padding auto 값들을 사용하여 컨텐츠 정렬을 해야 한다.
3) 올바른 언어인가?
해당 언어가 잘못된 말인지 개발자나 QA도 알 수가 없다. 번역된 언어에 대해서 신뢰하고 넘어갈 수밖에 없다.
배포 후에 잘못된 언어를 발견하면 새로 배포를 해서 수정해야 될 텐데, 이런 부분에 있어서 다양한 전략을 갖고 운영을 해야 한다.
이전 회사에서는 S3에 번역 언어들을 관리해서 개발자의 추가적인 배포 없이 번역 시스템에서 수정하면 S3에 있는 번역어들을 최신화하는 전략을 사용했다.
운영 이슈
다국어 앱을 운영하는 것은 고려할 것이 많다.
아래는 국내 서비스를 운영할 때 공지사항 하나가 추가될 때 운영자가 하던 방식이다.
- 공지사항 노션 페이지 생성
- 배너 이미지 피그마로 생성
- 백오피스를 통해 이미지 업로드 및 노션 페이지 링크 입력
- 업로드
국내 서비스인 경우는 매우 단순한 플로우다.
글로벌 서비스를 운영하면서는 공지사항 업로드가 매우 어려운 작업이 되었다.
- 한국어로 공지사항 작성
- 18개 언어로 번역
- 번역된 언어별로 18개의 노션 페이지 생성
- 18개 언어로 번역한 배너 이미지 피그마로 생성
- 백오피스를 통해 18개 이미지 업로드 및 노션 페이지 링크 입력
- 업로드
운영하던 팀원 말로는 공지사항 하나 업로드하려면 3시간 이상 소요된다고 했다. 정말 글로벌 서비스 운영은 쉽지 않다.
(해당 문제는 자동화를 구축하여 딸깍 할 수 있게 되었는데, 이 부분은 나중에 블로그 글을 따로 작성해봐야겠다)
정리
개발적으로:
- ICU와 Intl API. 브라우저 엔진에 이미 국제화 기능들이 구현되어 있다. 날짜, 숫자, 통화 포맷은 라이브러리 없이도 처리 가능하다.
- 복수형 규칙. 언어마다 복수형 규칙이 다르다. 러시아어는 4가지 형태가 있다. ICU Message Format이 이런 복잡함을 표준화된 문법으로 해결한다.
- 언어별 줄바꿈 규칙. CJK와 Non-CJK가 다르고, 한국어와 일본어도 미묘하게 다르다. overflow-wrap, word-break, line-break를 상황에 맞게 조합해야 한다.
- 언어별 폰트 높이. 고정 높이 + padding 조합은 특정 언어에서 텍스트가 잘린다. flex나 auto 값으로 유연하게 대응해야 한다.
- 번역 플랫폼. Crowdin, Phrase 같은 플랫폼들이 ICU 문법을 지원한다. 번역가들이 직접 복수형 규칙을 작성할 수 있다.
개발 외적으로:
- QA 범위. 18개 언어를 지원하면 QA 범위가 상상 이상으로 넓어진다. 러시아어 같은 긴 언어는 레이아웃을 완전히 바꿔야 하는 경우도 있다.
- 번역 신뢰. 개발자도 QA도 해당 언어를 모르기 때문에 번역이 올바른지 검증할 방법이 없다. 배포 후 수정을 위한 전략이 필요하다.
- 운영 복잡도. 공지사항 하나 올리는데도 복잡한 플로우가 생겨난다. 다양한 전략으로 운영 이슈를 해결해야한다.
글로벌 서비스는 단순히 언어만 바꾸는 게 아니다. 기술, 운영, QA, 번역 프로세스 모든 것이 복잡하다.
