How to translate a game’s .po and .mo files (gettext)
The .po is the editable source; the .mo is the compiled catalog the game actually loads — which is why editing the .po alone changes nothing. RuneTranslate reads both, protects %s and {count} placeholders, refits plural forms to your language, and writes a working .mo back.
게임 폴더를 열었더니 .po와 .mo 파일로 가득한 locale 디렉터리가 있었다면, 그 게임은 gettext로 현지화된 것입니다. 그리고 이제 RuneTranslate가 프로젝트 생성부터 플레이 가능한 빌드 내보내기까지 처음부터 끝까지 번역할 수 있습니다.
이 글은 그 두 파일이 실제로 무엇인지, 읽을 수 있는 쪽 파일을 고쳐도 게임에서는 대개 아무것도 바뀌지 않는 이유, 그리고 제대로 번역하는 방법을 설명합니다.
.po와 .mo — 모두가 걸려 넘어지는 차이
둘은 같은 카탈로그의 두 가지 형태입니다.
- `.po` — Portable Object. 어떤 편집기로든 열 수 있는 일반 텍스트 파일입니다. 각 항목은
msgid(원문)와msgstr(번역)로 이루어집니다. 번역자가 작업하는 파일이 이것입니다. - `.mo` — Machine Object. 같은 데이터를 게임이 즉시 찾아볼 수 있게 배치한 컴파일된 바이너리 버전입니다. 실행 중인 게임이 읽는 파일이 바로 이것입니다.
마지막 항목이 많은 사람의 반나절을 잡아먹습니다. .po의 모든 줄을 번역해 저장하고 게임을 실행해도 아무 변화가 없을 수 있습니다. 게임은 .po를 아예 열지 않기 때문입니다. 옆에 있는 .mo를 불러오는데, 거기에는 여전히 예전 문구가 들어 있습니다. 보통은 GNU gettext 도구의 msgfmt로 한쪽을 다른 쪽으로 컴파일합니다. RuneTranslate는 둘 다 기록하므로 별도의 컴파일 단계가 없습니다.
gettext 게임 폴더의 모습
표준 구조는 언어 코드로 나뉜 locale 디렉터리입니다.
MyGame/game.exeMyGame/locale/en/LC_MESSAGES/mygame.moMyGame/locale/fr/LC_MESSAGES/mygame.moMyGame/locale/de/LC_MESSAGES/mygame.mo
LC_MESSAGES 폴더는 실수가 아니라 관례의 일부이고, mygame은 텍스트 도메인 — 게임이 문자열을 불러올 때 요청하는 이름입니다. 많은 게임은 대신 lang/fr.po 같은 더 평평한 형태를 쓰는데, 그것도 동작합니다.
어떤 게임이 gettext를 쓰나요?
gettext는 유닉스 세계에서 나와 C, Python, GTK 기반 소프트웨어를 현지화하는 기본 방식이 되었습니다. 게임에서는 다음과 같습니다.
- Python과 pygame 게임
- Solarus, SDL 등 C/C++ 엔진
- 모두 PO를 기본으로 다루는 Poedit, Weblate, Crowdin을 거쳐 현지화된 게임
- PO를 번역 원본으로 지원하는 일부 Godot 프로젝트
일본 게임보다 서구 인디 게임에서 훨씬 흔합니다. Kirikiri나 RPG Maker 같은 일본 엔진은 텍스트를 각자의 형식으로 보관합니다.
RuneTranslate로 gettext 게임 번역하기
- RuneTranslate를 게임 폴더로 지정하세요. 안에
locale/,lang/또는i18n/이 있는 폴더입니다. 감지는 확장자를 믿는 대신 실제 바이트를 읽으므로, 사실은 3D 장면인.mo는 깨진 프로젝트를 만드는 대신 무시됩니다. - 요약을 확인하세요. 프로젝트를 만들기 전에 카탈로그 수, 컴파일된
.mo와 원본.po의 비율, 게임이 이미 포함한 언어, 전체 문자열 수를 볼 수 있습니다. - 언어와 번역 서비스를 고르세요. 무료 Google이나 DeepL, 또는 본인의 DeepL·OpenAI·Claude·DeepSeek 키 — 서비스 비교를 참고하세요.
- 검토한 뒤 내보내세요. 번역이 적용된 게임 전체 사본을 받게 됩니다.
gettext 번역을 조용히 망가뜨리는 두 가지
자리표시자. msgid는 완성된 문장이 아니라 서식 문자열입니다. Hello, %s!에서는 실행 시점에 플레이어 이름이 들어갑니다. 기계 번역은 %s를 지우거나 사이를 벌리거나, Python의 이름 있는 형태인 %(player)s에서는 안쪽 단어까지 번역해 버리곤 합니다. 결과는 보기 문제에 그치지 않습니다. %s 하나가 빠지면 뒤따르는 인자가 전부 밀리고 대부분의 엔진은 그대로 죽습니다. RuneTranslate는 찾아낸 모든 자리표시자(%s, %d, %(player)s, {count}, {0})를 서비스로 보내기 전에 가리고 번역 후 되돌립니다.
복수형. 영어는 둘(“1 coin” / “5 coins”), 일본어와 터키어는 하나, 러시아어와 폴란드어는 셋입니다. 카탈로그는 헤더에 몇 개를 쓰는지 선언하는데, 그 수가 언어와 맞지 않으면 gettext는 목록 끝을 넘어 접근하고 조용히 번역되지 않은 원문으로 되돌아갑니다. RuneTranslate는 대상 언어에 맞게 형태를 다시 맞추고 헤더도 그에 맞춰 씁니다.
내보내기가 카탈로그를 두 번 쓰는 이유
내보내기는 올바른 새 언어 — locale/<선택한 언어>/LC_MESSAGES/ — 와, 게임이 이미 불러오고 있던 카탈로그의 갱신본을 함께 만듭니다.
번역이 실제로 나타나게 하는 것은 후자입니다. 게임이 어떤 로케일을 불러올지는 폴더만 봐서는 알 수 없는 방식으로 정해집니다. 운영체제 언어를 읽을 수도, 설정 파일이나 실행기 플래그를 볼 수도, 아예 코드에 박아뒀을 수도 있습니다. 영어만 요청하는 게임에 한국어 카탈로그를 더해봐야 겉보기에 전혀 달라지지 않은 빌드를 바라보게 될 뿐입니다. 실제로 불러오는 카탈로그를 덮어쓰면 그 추측이 사라집니다. 둘 다 내보낸 사본에서만 일어나며, 원래 게임 폴더는 수정되지 않습니다.
번역하지 않은 문자열은 게임이 원래 갖고 있던 번역을 유지하므로, 절반쯤 진행된 프로젝트가 개발자의 현지화를 비워버리는 일은 없습니다.
이것으로 할 수 없는 일
이미 카탈로그에 있는 텍스트만 번역할 수 있습니다. 개발자가 문자열을 gettext에 통과시키지 않고 코드에 직접 써 넣었다면 그것은 .po에도 .mo에도 없으며, 어떤 PO 도구도 거기서 찾아낼 수 없습니다. 도구가 아니라 형식의 한계입니다.
RuneTranslate는 또한 msgid를 원문으로 취급합니다. gettext 자체가 그렇게 정하고 있기 때문입니다. 일부 프로젝트는 msgid를 영어로 두고 실제 원어를 msgstr에 넣는데, 그런 경우 영어에서 번역됩니다.
끝으로 게임이 직접 지원되는 엔진 — Godot, Unity, Ren’Py — 위에서 돌아간다면 그 엔진이 처리합니다. 카탈로그뿐 아니라 게임의 나머지 텍스트에도 닿을 수 있도록 하기 위해서입니다.
공유하기 전에
내보낸 빌드를 실행해 몇 화면을 읽어 보세요. 카탈로그 텍스트 대부분이 있는 곳이라 메뉴와 버튼이 가장 빠른 확인 방법입니다. 문제가 있다면 — 게임이 감지되지 않거나, 내보낸 빌드가 실행되지 않거나, 텍스트가 여전히 원래 언어라면 — 앱의 도움말 메뉴나 Discord로 알려주세요. RuneTranslate 내려받기 후 첫 카탈로그를 열어 보세요.
RuneTranslate를 써볼 준비가 되셨나요?
무료 등급에서 모든 엔진과 모든 번역 제공자가 열립니다. Supporter($3/mo)로 최고 속도가 열립니다.
Windows용 다운로드