
지난달에 앱에 영어를 처음 붙이는데, 'Settings' 한 단어가 버튼 폭을 뚫고 나가더라고요. 6년차인데도 그 화면 앞에서 한참을 멍하니 앉아있었습니다.
처음엔 "번역만 넣으면 되겠지" 싶었어요. 근데 막상 해보니까 번역은 전체 작업의 절반도 안 됐습니다. 진짜 일은 코드 곳곳에 박힌 한국어를 끄집어내고, 화면이 영어 길이에 안 터지게 다듬는 거였어요.
그때 제가 헤맸던 자리들을 순서대로 풀어볼게요.
안드로이드 다국어, 번역보다 하드코딩 문자열 추출이 먼저예요
다국어 작업의 핵심은 번역이 아니라, 앱 전체에 흩어진 하드코딩 문자열을 res/values/strings.xml로 옮기는 일입니다. 신규 기능 만들 때보다 옛날 코드 뒤지는 데 시간이 훨씬 더 들더라고요.
기본 한국어는 values/strings.xml, 영어는 values-en/strings.xml에 같은 키로 넣어두면 됩니다. 그러면 시스템 언어에 따라 폴더 한정자(-en, -ja)로 알아서 골라주거든요.
코드에서는 항상 이렇게 참조해야 로케일이 먹어요.
// 하드코딩 "설정" 대신 키 참조 — 로케일에 따라 자동 선택
val title = getString(R.string.settings_title)
안드로이드 13(Android 13)부터는 시스템 설정에서 앱별로 언어를 따로 지정할 수 있게 됐습니다. 런타임에 언어를 바꾸려면 AppCompat 1.6.0 이상에서 이렇게 쓰면 되는데, 하위 버전 호환까지 챙겨줍니다.
// AppCompatDelegate 로 호출하면 13 미만에서도 동작
AppCompatDelegate.setApplicationLocales(
LocaleListCompat.forLanguageTags("en")
)
다만 시스템 설정 안의 "앱 언어 선택" 화면 자체는 13 미만에선 안 보입니다. API 호출만 호환되는 거지, UI까지 내려오는 건 아니라는 점은 짚어두셔야 해요.

plurals 복수형과 위치 인자, 런타임 크래시 나는 자리
여기가 제가 진짜로 한 방 먹은 곳입니다.
"사진 3개"처럼 숫자에 따라 말이 바뀌는 문자열은 string-array가 아니라 plurals 리소스를 써야 합니다. 영어는 one이랑 other 둘만 있으면 되는데, 문제는 other가 모든 언어의 필수 항목이라는 점이에요. 이걸 빼먹으면 그냥 런타임에 크래시가 터집니다.
<plurals name="photo_count">
<item quantity="one">%d photo</item>
<item quantity="other">%d photos</item>
</plurals>
호출은 resources.getQuantityString(R.plurals.photo_count, count, count), 젯팩 컴포즈(Jetpack Compose)에선 pluralStringResource(R.plurals.photo_count, count, count)로 받으면 됩니다.
그리고 값이 두 개 이상 들어가면 %1$s, %2$d 같은 위치 인자를 반드시 쓰세요. 언어마다 어순이 달라서, 번역가가 순서를 바꿔야 자연스러운 문장이 나오거든요. 그냥 %s로만 두면 어순을 못 바꿔서 어색한 영어가 됩니다.

의사 로케일과 RTL로 번역 전에 화면 깨짐 잡기
번역가한테 넘기기 전에 깨짐을 미리 잡는 방법이 있는데, 의사 로케일(Pseudolocale)이라고 합니다.
en-XA 로케일을 켜면 글자를 [Lôñgér téxt] 같은 형태로 늘려서 보여줘요. 번역을 안 해도 텍스트가 길어졌을 때 버튼이 터지는지, 빠뜨린 문자열이 어디 있는지 한눈에 보입니다. Gradle에서 pseudoLocalesEnabled = true로 켜두시면 됩니다.
처음에 제가 'Settings'에서 당했던 것도, 이걸 먼저 돌려봤으면 번역 들어가기 전에 잡았을 거예요.
아랍어나 히브리어까지 갈 거면 RTL(오른쪽에서 왼쪽으로 읽는 레이아웃)도 챙겨야 하는데요. 매니페스트에 android:supportsRtl="true"를 넣으면 좌우가 반전됩니다. 이때 레이아웃 속성을 left/right로 박아뒀으면 다 깨져요. start/end로 바꿔두는 게 권장 방식입니다.

날짜·통화 현지화까지 챙겨야 진짜 다국어입니다
문자열만 번역하면 끝이라고 생각하기 쉬운데, 막상 해보니 그게 전부가 아니었어요.
날짜만 해도 미국은 06/21/2026, 한국은 2026/06/21로 순서가 다릅니다. 통화 기호(₩, $), 숫자 천 단위 구분까지 다 현지화 대상이에요. 이런 건 직접 문자열 조립하지 말고 로케일 기반 포매터에 맡기는 게 맞습니다.
개인적으로는 여기서 욕심을 좀 줄이시라고 말씀드리고 싶어요. 국내 전용 소규모 앱이라면 RTL이나 통화 현지화까지 미리 깔아두는 건 과투자일 수 있거든요. 저도 처음엔 다 완벽하게 하려다 일정만 늘어졌습니다.
대신 언어를 추가할 때마다 모든 strings.xml 파일의 키를 똑같이 맞춰줘야 하는 관리 비용은 계속 따라옵니다. AGP 8.1 이상이라면 androidResources { generateLocaleConfig = true }로 켜두면 빌드 과정에서 로케일 설정을 자동 생성해주니, 지원 언어 목록 관리는 그걸로 덜 수 있어요.
결국 다국어 지원은 번역 프로젝트가 아니라, 코드에 박힌 "이 앱 사용자는 한국어를 쓴다"는 가정을 하나씩 걷어내는 작업이었습니다. 그래서 처음 영어를 붙이실 거면 순서를 거꾸로 잡으셔야 해요. 번역 파일부터 만들지 말고, 의사 로케일 한 번 돌려서 내 화면이 어디서 터지는지부터 확인하는 겁니다. 저도 그날 그 순서만 알았어도 'Settings' 앞에서 멍 때리는 30분은 아꼈을 텐데 말이죠.

'Android 개발 > 트러블슈팅' 카테고리의 다른 글
| Android 17 Contact Picker로 갈아탄 자리 — READ_CONTACTS 막히기 전 마이그레이션 일지 (0) | 2026.07.31 |
|---|---|
| Paging 3 무한 스크롤로 끊김 잡은 자리 — Room 캐시까지 한 번에 (0) | 2026.07.30 |
| 안드로이드 배터리 드레인, 밤새 깨어 있던 8시간을 추적한 기록 (0) | 2026.07.25 |
| SharedPreferences를 DataStore로 옮긴 110ms 사냥 기록 (1) | 2026.07.24 |
| Room 느린 쿼리, Database Inspector로 풀스캔 잡은 기록 (1) | 2026.07.23 |