70.1 문장을 이어붙였다 번역이 왜 깨져요?
국제화(여러 나라 말과 문화에 맞추기)를 처음 하면 거의 모두가 밟는 지뢰가 있어요. 바로 문자열을 손으로 이어붙이기예요. 제 첫 삽질담이에요. "홍길동님이 3개의 댓글을 남겼어요" 같은 문장을 만들려고, 저는 토막을 이어붙였어요.
상황: 문장을 조각내 이어붙이면 다른 말로 옮길 수가 없어요.
const msg = name + "님이 " + count + "개의 댓글을 남겼어요";
한국어일 땐 멀쩡했어요. 문제는 영어 번역을 붙일 때 터졌죠. 영어는 어순이 딴판이라 "홍길동 left 3 comments"처럼 이름과 숫자의 자리가 달라요. 그런데 코드가 "이름 + 님이 + 숫자 + 개의..." 순서로 못 박혀 있으니, 번역가가 어순을 바꿀 방법이 없어요.
정답은 문장을 통째로 번역 대상으로 두고, 값이 들어갈 자리만 표시하는 거예요. 이 자리 표시를 플레이스홀더(placeholder, 값이 나중에 채워질 빈칸)라고 해요.
상황: 문장은 통째로, 빈칸만 뚫어두면 각 언어가 어순을 자유롭게 정해요.
// ko: "{name}님이 댓글 {count}개를 남겼어요"
// en: "{name} left {count} comments"
const msg = t("comment_left", { name, count });
이제 각 언어가 자기 어순대로 문장 전체를 갖고, 빈칸만 값으로 채워요. 규칙은 하나예요. 번역의 단위는 단어가 아니라 문장이다.
70.2 화면에 문구를 그냥 박아버렸어요
두 번째 단골 실수는 하드코딩(hardcoding, 문구를 코드에 직접 박기)이에요. 앞 편에서 색을 박으면 안 된다고 했죠? 글자도 똑같아요. 급하다고 버튼에 "저장"이라고 직접 적으면, 나중에 영어를 붙일 때 그 글자가 어디 숨어 있는지 찾아 헤매요.
제 경험담이에요. 번역을 붙이는 날, 저는 "화면의 모든 한국어를 찾아 바꾸면 되겠지" 했어요. 그런데 alert 메시지, 버튼 라벨, 툴팁에 박아둔 한국어가 수백 군데라 며칠을 뒤졌어요. 몇 개는 끝내 놓쳐서 영어 화면에 한국어가 튀어나왔죠.
상황: 왼쪽은 나중에 못 찾는 지뢰, 오른쪽은 사전에서 꺼내 쓰는 문구.
// 이렇게 하지 마세요
button.textContent = "저장";
// 이렇게 하세요
button.textContent = t("save"); // 언어별 사전에서 꺼냄
해법은 모든 문구를 사전 파일 한곳에 모으는 거예요. ko.json에 "save": "저장", en.json에 "save": "Save"처럼 언어별 사전을 두고, 코드는 t("save")로 열쇠(key)만 부르는 거죠. 이러면 새 언어를 붙일 때 사전 파일 하나만 추가하면 돼요. 앞 편 색상 토큰이랑 같은 원리예요. 값을 박지 말고 이름으로 불러라. 이 습관을 들인 뒤로 "화면에 딴 언어가 섞여요" 민원이 뚝 끊겼어요.
70.3 번역했더니 글자가 넘쳐요
이건 눈으로 봐야 실감 나는 함정이에요. 한국어로는 딱 맞던 버튼이, 다른 말로 바꾸니 글자가 넘쳐서 버튼을 뚫고 나가거나 두 줄로 깨지는 거예요. 언어마다 같은 뜻도 길이가 천차만별이거든요.
제가 데인 건 독일어였어요. 한국어 "설정"은 두 글자인데, 독일어 "Einstellungen"은 열세 글자예요. 아이콘 옆에 딱 맞춰 만든 버튼이 독일어에서 박살이 났죠. 반대로 한국어나 중국어는 짧아서 영어 기준으로 만든 칸이 휑하게 비기도 해요.
상황: 글자가 넘쳐도 칸이 안 깨지게, 폭을 고정하지 말고 여유를 둬요.
.button {
min-width: 80px; /* 고정 width 대신 최소만 */
padding: 8px 16px;
white-space: normal; /* 필요하면 줄바꿈 허용 */
}
핵심은 "글자 길이를 예측하지 마라"예요. width를 딱 고정하지 말고 min-width에 여유 padding으로 늘어날 자리를 주세요. 한 가지 팁을 더 드리면, 디자인할 때 영어 기준에 30퍼센트쯤 더 길어질 수 있다고 잡고 칸을 넉넉히 만들어두면, 나중에 어떤 언어가 들어와도 덜 깨져요. 텍스트가 넘칠 때 ...으로 자를지, 줄을 바꿀지도 미리 정해두면 좋고요.
70.4 날짜가 나라마다 딴판이에요?
이 함정은 버그로 안 보여서 더 무서워요. 날짜 03/04/2026을 볼게요. 이게 3월 4일일까요, 4월 3일일까요? 미국은 월/일/년이라 3월 4일로, 유럽 대부분은 일/월/년이라 4월 3일로 읽어요. 같은 글자가 다른 날이 되는 거예요.
제 삽질담이에요. 날짜를 제가 직접 문자열로 조립했어요. month + "/" + day + "/" + year처럼요. 한국 사용자에겐 순서가 어색했고, 미국 사용자는 유럽식으로 오해했어요. 손으로 조립한 날짜는 이 지뢰를 절대 못 피해요.
상황: 날짜 형식을 손으로 만들지 말고, 지역에 맞는 형식을 브라우저에 맡겨요.
const d = new Date(2026, 2, 4);
d.toLocaleDateString("en-US"); // "3/4/2026"
d.toLocaleDateString("ko-KR"); // "2026. 3. 4."
d.toLocaleDateString("de-DE"); // "4.3.2026"
toLocaleDateString에 지역 코드(en-US 같은)만 넘기면, 그 나라가 쓰는 순서와 구분자로 브라우저가 알아서 찍어줘요. 날짜만이 아니에요. 숫자의 천 단위 구분(1,000 대 1.000), 통화 기호, 시간대도 지역마다 달라서, 브라우저의 Intl(국제화 도우미) 기능에 맡기는 게 정답이에요. 사람이 손으로 짜면 반드시 어느 나라에선가 틀려요.
70.5 한글이 깨져서 물음표로 떠요
이건 고전 중의 고전이에요. 한글이나 이모지가 ???나 네모, 아니면 가나다 같은 외계어로 뜨는 거요. 원인은 대개 인코딩(encoding, 글자를 바이트로 저장하고 읽는 약속)이 어긋난 거예요.
제가 신입 때 반나절을 태운 사건이에요. DB에 한글을 잘 넣었는데 화면에서 다 깨져 나왔어요. 알고 보니 파일은 UTF-8(모든 글자를 담는 표준 인코딩)로 저장했는데, HTML이 다른 인코딩으로 읽고 있었던 거예요. 쓴 약속과 읽는 약속이 달라서, 같은 바이트를 엉뚱하게 해석한 거죠.
상황: 문서를 UTF-8로 읽으라고 맨 위에서 못을 박아둬요.
<head>
<meta charset="UTF-8">
</head>
규칙은 단순해요. 모든 곳을 UTF-8로 통일하라. HTML의 meta charset, 파일 저장 인코딩, DB 문자셋, 서버가 보내는 Content-Type 헤더까지 전부 UTF-8이어야 해요. 한 군데라도 다른 약속을 쓰면 그 지점에서 글자가 깨져요. 요즘은 UTF-8이 기본이라 덜 겪지만, 파일을 옮기거나 옛 시스템과 주고받을 때 잘 나니 "깨지면 인코딩부터 의심"이라고 외워두세요.
70.6 "1개"랑 "5개"를 어떻게 처리해요?
마지막은 복수형(단수와 복수에 따라 말이 바뀌는 것)이에요. 한국어는 운이 좋아요. "1개"든 "5개"든 "개"로 똑같거든요. 그래서 우리는 이 함정을 잘 몰라요. 그런데 영어는 1 item, 2 items처럼 단수와 복수의 단어가 달라요.
초보는 이걸 if로 대충 때워요. "1이면 item, 아니면 items" 하고요. 영어까진 버텨요. 그런데 러시아어, 아랍어, 폴란드어 같은 언어는 복수형이 둘이 아니라 서너 개예요. 숫자가 1일 때, 2에서 4일 때, 5 이상일 때 단어가 제각각 바뀌죠. 제가 if로 짰다가 이 언어들에서 전부 틀린 문구가 나갔어요.
상황: 복수 규칙을 직접 짜지 말고, 언어별 복수 형태를 사전에 맡겨요.
// en 사전: { one: "{n} item", other: "{n} items" }
// ko 사전: { other: "{n}개" }
const msg = t("item_count", { n: count });
해법은 복수 규칙을 내가 짜지 않는 거예요. 국제화 도구(i18n 라이브러리)와 브라우저의 Intl.PluralRules가 언어별 복수 규칙을 이미 알고 있어요. 우리는 사전에 one, other 같은 복수 형태별 문구만 채워두면, 숫자에 맞는 형태를 도구가 골라줘요. 규칙 자체는 도구가 책임지니, 우리는 번역 칸만 채우면 끝이에요.
70.7 오늘 국제화 실수들을 정리해볼게요
제가 몸으로 배운 국제화 지뢰 여섯을 모았어요. 다 데이는 것들이에요.
문장을 이어붙이지 말고 통째로 번역해 빈칸만 채우세요. 문구를 코드에 박지 말고 언어별 사전에서 꺼내 쓰세요. 번역하면 글자가 넘치니 폭을 고정하지 말고 여유를 두세요. 날짜, 숫자, 통화는 손으로 조립하지 말고 지역 기능에 맡기세요. 글자가 깨지면 인코딩을 UTF-8로 통일했는지 보세요. 복수형은 if로 때우지 말고 언어별 규칙을 도구에 맡기세요.
여섯 실수엔 공통점이 있어요. 전부 "내 언어와 문화가 세상의 기본"이라는 착각에서 나와요. 한국어만 보면 조각을 이어붙여도, 날짜를 직접 짜도, 복수를 무시해도 멀쩡해 보여요. 그래서 국제화를 잘하는 사람은 자기 언어를 특별 취급하지 않아요. 어순도 길이도 형식도 복수 규칙도 나라마다 다르다는 걸 전제로 깔고, 손으로 짜는 대신 도구에 맡겨요. 이 습관 하나가 새 언어를 붙일 때마다 터지던 사고를, 사전 파일 하나 추가로 조용히 끝나게 만들어요.