안드로이드를 만들며 13 — 검사는 겨눈 것만 셉니다 - 11개 언어와 strings-parity의 맹점
11개 언어 리소스의 누락을 잡는 strings-parity.sh 스크립트가 있었습니다. 스크립트는 항상 초록불이었지만, 실제 스페인어 파일에는 영어가 섞여 있고 프랑스어 파일에는 스페인어가 오염되어 있었습니다. 검사는 열쇠만 세지, 그 안의 뜻을 보지 못합니다.
글로벌 출시를 위해 11개 언어(한국어, 영어, 일본어, 중국어 간/번체, 스페인어, 독일어, 프랑스어, 이탈리아어, 포르투갈어, 인도네시아어)를 지원하고 있습니다.
언어가 열한 벌이나 되다 보니 새 기능이 들어갈 때마다 번역 키가 누락되기 십상입니다. 그래서 strings-parity.sh라는 CI 검증 스크립트를 만들어 돌렸습니다.
# strings-parity.sh의 핵심 로직
# 기본 언어(ko/en)의 모든 <string name="..."> 키가 다른 10개 언어 파일에도 존재하는지 검사
매 빌드마다 이 검사는 아주 씩씩하게 초록불(Exit 0)을 띄웠습니다. “11개 언어 240개 키의 동기화가 완벽하다”고 믿었습니다.
스페인어 화면에 영어가 남아 있던 이유
어느 날 스페인어(values-es/strings.xml) 리소스를 직접 열어보다가 깜짝 놀랐습니다.
키는 빠짐없이 다 정의되어 있었지만, 내용물이 이렇게 되어 있었습니다.
<!-- values-es/strings.xml -->
<string name="action_scan_document">Scan Document</string>
<string name="msg_delete_confirm">¿Estás seguro de que deseas eliminar?</string>
<string name="btn_upgrade_pro">Upgrade to PRO</string>
- 스크립트의 시선:
action_scan_document키가 존재하는가? → Yes (통과) - 사용자의 시선: 스페인어로 앱을 쓰는데 중요한 버튼만 영어로 나온다 → 번역 누락 (버그)
strings-parity.sh는 열쇠(Key)의 존재 여부만 셀 뿐, 그 열쇠가 품은 값(Value)의 오염은 검사하지 못했습니다.
기능을 급히 추가할 때 일단 빌드를 통과시키려고 기본 언어 텍스트를 복사해 넣었던 것이 그대로 프로덕션까지 살아남은 것입니다.
겹치는 낱말을 기계적으로 지우면 생기는 2차 사고
이 문제를 기계적으로 해결하려고 “기본 언어(영어)와 문자열 값이 100% 일치하면 에러를 뱉자”는 스크립트를 짰다면 또 다른 참사가 났을 것입니다.
실제로 다국어를 뜯어보면 서로 다른 언어가 완전히 같은 철자의 단어를 쓰는 경우가 많습니다.
- ‘Cancelar’: 스페인어와 포르투갈어 모두 취소 버튼으로 사용합니다.
- ‘Information’: 영어와 프랑스어(철자 동일)에서 모두 쓰입니다.
- ‘PDF’, ‘PRO’, ‘OK’: 모든 언어에서 동일하게 유지되어야 하는 공통 명사입니다.
만약 단순 텍스트 일치율로 기계적 필터링을 걸었다면 멀쩡하고 정확한 번역본까지 모조리 파괴했을 것입니다.
번역 빚을 털어내는 3단계 정비 원칙
우리는 11개 언어의 번역 빚을 다음과 같은 원칙으로 전수 청산했습니다.
- 기계가 찾아주고 사람이 한 줄씩 봅니다: 동일 문자열 후보군을 스크립트로 추출하되, 실제 수정 여부는 언어별 문맥을 아는 사람이 하나씩 대조하며 확정했습니다.
- 언어별 독립성 보장: 포르투갈어와 스페인어가 같은 단어를 쓰더라도, 참조 링크로 묶지 않고 각 언어 파일에 명시적으로 독립된 값으로 둡니다.
- 더미 값 전용 프리픽스 도입: 미번역 임시 문자열에는
TODO_TRANSLATE_같은 명시적 마킹을 붙여 CI가 키뿐만 아니라 플레이스홀더를 감지하도록 개선했습니다.
다시 같은 일을 한다면
- 검사는 겨눈 것만 셉니다. 키가 있다고 해서 번역이 된 것은 아닙니다.
- 기계적인 검증 스크립트는 뼈대를 지켜줄 뿐, 콘텐츠의 질감과 정확성은 사람이 직접 읽어야 합니다.
- 겉보기에 정갈한 XML 파일 뒤에 숨은 ‘복사-붙여넣기의 잔재’를 정기적으로 감사(Audit)하세요.