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.
ถ้าคุณเปิดโฟลเดอร์เกมแล้วพบไดเรกทอรี locale ที่เต็มไปด้วยไฟล์ .po และ .mo แสดงว่าเกมนั้นทำภาษาด้วย gettext — และตอนนี้ RuneTranslate แปลเกมแบบนั้นได้ครบวงจร ตั้งแต่สร้างโปรเจกต์ไปจนถึงส่งออกบิลด์ที่เล่นได้
คู่มือนี้อธิบายว่าไฟล์สองชนิดนั้นคืออะไรกันแน่ ทำไมการแก้ไฟล์ที่คุณอ่านออกมักไม่ทำให้อะไรเปลี่ยนในเกม และจะแปลอย่างถูกต้องได้อย่างไร
.po กับ .mo — ความต่างที่ทุกคนสะดุด
ทั้งสองเป็นสองรูปแบบของแคตตาล็อกเดียวกัน:
- `.po` — Portable Object ไฟล์ข้อความธรรมดาที่เปิดด้วยโปรแกรมแก้ไขใดก็ได้ แต่ละรายการมี
msgid(ข้อความต้นฉบับ) และmsgstr(คำแปล) นี่คือไฟล์ที่นักแปลทำงานด้วย - `.mo` — Machine Object เวอร์ชันไบนารีที่คอมไพล์แล้วของข้อมูลชุดเดียวกัน จัดวางให้เกมค้นหาข้อความได้ทันที นี่คือไฟล์ที่เกมอ่านจริงขณะทำงาน
ข้อสุดท้ายนี่แหละที่ทำให้หลายคนเสียเวลาไปทั้งบ่าย คุณอาจแปลทุกบรรทัดใน .po บันทึก แล้วเปิดเกม — แต่ไม่เห็นอะไรเปลี่ยนเลย เพราะเกมไม่เคยเปิด .po มันโหลด .mo ที่อยู่ข้าง ๆ ซึ่งยังเก็บข้อความเดิมไว้ ปกติคุณต้องคอมไพล์จากไฟล์หนึ่งไปอีกไฟล์ด้วย msgfmt จากชุดเครื่องมือ GNU gettext ส่วน 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++ อื่น ๆ
- เกมที่ทำภาษาผ่าน Poedit, Weblate หรือ Crowdin ซึ่งล้วนรองรับ PO โดยตรง
- โปรเจกต์ Godot บางส่วนที่รองรับ PO เป็นแหล่งคำแปล
พบได้บ่อยกว่ามากในเกมอินดี้ฝั่งตะวันตกเมื่อเทียบกับเกมญี่ปุ่น — เอนจินญี่ปุ่นอย่าง Kirikiri และ RPG Maker เก็บข้อความไว้ในรูปแบบของตัวเอง
แปลเกม gettext ด้วย RuneTranslate
- ชี้ RuneTranslate ไปที่โฟลเดอร์เกม โฟลเดอร์ที่มี
locale/,lang/หรือi18n/อยู่ข้างใน การตรวจจับอ่านไบต์จริงแทนที่จะเชื่อนามสกุล ดังนั้นไฟล์.moที่จริงแล้วเป็นฉาก 3 มิติจะถูกข้ามไป แทนที่จะกลายเป็นโปรเจกต์ที่พัง - ตรวจสรุปผล ก่อนสร้างโปรเจกต์ คุณจะเห็นจำนวนแคตตาล็อก สัดส่วนระหว่าง
.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