ทำไมข้อความที่แปลแล้วขึ้นเป็นสี่เหลี่ยม
คุณแปลเกมเสร็จ ส่งออกผ่านฉลุย แล้วกล่องบทสนทนาเต็มไปด้วย □□□ นั่นแทบไม่เคยเป็นปัญหาของการแปล — มันคือปัญหาฟอนต์ และวิธีแก้ต่างจากอีกสองอาการที่คนมักสับสน นี่คือวิธีแยกกลิฟที่หายไป ออกจากอักขระที่ถูกตัดทิ้ง และออกจาก mojibake พร้อมวิธีแก้แต่ละแบบใน Unity, TextMeshPro, Ren'Py และเครื่องมือตอนรันไทม์
การส่งออกเสร็จเรียบร้อย เกมเปิดขึ้นมา แล้วบทสนทนาทุกบรรทัดกลายเป็นแถวของสี่เหลี่ยมว่างเปล่าที่เหมือนกันหมด ไม่มีอะไรผิดเกี่ยวกับคำแปลเลย ข้อความในไฟล์คือสิ่งที่ผู้ให้บริการคืนมาทุกประการ และถ้าคุณเปิดไฟล์นั้นในโปรแกรมแก้ไขข้อความ มันอ่านได้ถูกต้อง สิ่งที่ล้มเหลวคือขั้นสุดท้ายของสายงาน: ฟอนต์ที่เกมส่งให้ตัวเรนเดอร์ไม่มีรูปวาดสำหรับอักขระเหล่านั้น
กล่องเต้าหู้คืออะไรกันแน่
ฟอนต์คือตารางค้นหาจากโค้ดพอยต์ Unicode ไปยังรูปร่าง เมื่อตัวเรนเดอร์ถูกสั่งให้วาดโค้ดพอยต์ที่ฟอนต์ไม่มี มันจะแทนด้วย กลิฟที่หายไป ของฟอนต์นั้น — มักเป็นสี่เหลี่ยมว่างหรือสี่เหลี่ยมกากบาท ชื่อทางการคือ .notdef และที่อื่น ๆ เรียกกันว่า กล่องเต้าหู้ (tofu box) หนึ่งกล่องเท่ากับหนึ่งอักขระที่ฟอนต์วาดไม่ได้ กลไกมีเท่านั้นทั้งหมด
ผลตามมาสองข้อ และทั้งสองข้อมีประโยชน์ในการวินิจฉัย ข้อแรก จำนวนกล่องตรงกับจำนวนอักขระ: อักขระไทยห้าตัวให้กล่องห้ากล่อง ไม่ใช่ข้อความเพี้ยนที่สั้นลง ข้อสอง สตริงในหน่วยความจำยังสมบูรณ์ — เกมยังวัดขนาด เปรียบเทียบ บันทึก และโหลดกลับมาได้ หายไปแค่พิกเซลเท่านั้น นี่คือเหตุผลที่เมนูที่แปลแล้วอาจอ่านไม่ออกเลยแต่ยังกดใช้งานได้ถูกต้อง
สามอาการ สามปัญหาที่ต่างกัน
ความสับสนส่วนใหญ่รอบ ๆ "คำแปลของฉันพัง" คือคนเอาวิธีแก้ของอาการหนึ่งไปใช้กับอีกอาการหนึ่ง ดูรูปทรงของความเสียหายก่อนจะแก้อะไร
- กล่อง □□□ — อักขระถูกต้อง แต่ฟอนต์วาดมันไม่ได้ ให้แก้ที่ฟอนต์
- ???? — อักขระหายไปแล้ว มีบางอย่างเข้ารหัสสตริงใหม่ลงชุดอักขระที่แคบกว่า แล้วแทนทุกตัวที่แทนไม่ได้ ไม่มีฟอนต์ไหนเรียกมันกลับมาได้ ให้แก้ที่การเข้ารหัส หรือเลือกภาษาปลายทางที่รูปแบบไฟล์รองรับได้
- อักษรละตินเพี้ยน อย่าง
テã‚ストหรือテキストที่แสดงเป็นテキスト— mojibake ไบต์ถูกต้อง แต่ตัวถอดรหัสผิด: ข้อความที่เขียนด้วยการเข้ารหัสหนึ่งกำลังถูกอ่านด้วยอีกการเข้ารหัสหนึ่ง ให้แก้ว่ากำลังอ่านด้วยการเข้ารหัสใด
ตัวแยกเร็ว ๆ: ทั้งกล่องและ mojibake ต่างทิ้งจำนวนไบต์ไว้ใกล้เคียงสัดส่วนกับต้นฉบับ แต่ mojibake สร้างอักขระขยะที่ มองเห็นได้และหลากหลาย ในขณะที่กล่องเต้าหู้ให้สี่เหลี่ยมที่ เหมือนกันและสม่ำเสมอ สม่ำเสมอแปลว่า "ฟอนต์" หลากหลายแปลว่า "การเข้ารหัส"
กล่อง: กลิฟที่หายไป
เกมส่งมาพร้อมฟอนต์ที่ครอบคลุมเท่าที่มันต้องใช้ ไม่มีอะไรในสายงานพัง — คุณขอโค้ดพอยต์ที่ศิลปินไม่เคยใส่มา วิธีแก้คือให้ฟอนต์ที่มีกลิฟนั้นกับตัวเรนเดอร์เสมอ ไม่ว่าจะด้วยการแทนที่ฟอนต์ หรือด้วยการเพิ่มฟอนต์สำรองที่ตัวเรนเดอร์จะไปดูเมื่อฟอนต์หลักหากลิฟไม่เจอ
เครื่องหมายคำถาม: อักขระถูกตัดทิ้ง
เมื่อสตริงต้องถูกเขียนกลับลงในรูปแบบที่เก็บข้อความด้วยการเข้ารหัสแบบไบต์เดียวหรือสองไบต์รุ่นเก่า อักขระทุกตัวที่อยู่นอกการเข้ารหัสนั้นต้องกลายเป็นอะไรสักอย่าง ตามธรรมเนียมสิ่งนั้นคือ ? Shift-JIS และ cp932 เก็บภาษาญี่ปุ่น ASCII และอื่น ๆ ได้น้อยมาก ส่วนฟิลด์ ASCII แบบเข้มงวดเก็บกรีก ซีริลลิก ไทย หรือ CJK ไม่ได้เลย นี่เป็นคุณสมบัติของรูปแบบไฟล์ ไม่ใช่ของฟอนต์ และมันเกิดขึ้นตอนส่งออก — พอเกมอ่านไฟล์ ข้อมูลก็หายไปแล้ว
RuneTranslate มีเอนจินหนึ่งที่ข้อจำกัดนี้เกิดขึ้นจริง และเราระบุไว้ตรง ๆ แทนที่จะซ่อนมัน: YU-RIS เข้ารหัสสตริงกลับเป็น cp932 ซึ่งเป็นมิตรกับผลลัพธ์ภาษาอังกฤษและภาษาละตินอื่น ๆ แต่ส่งภาษาไทยออกมาเป็น ? ปลายทางที่ไม่ใช่ cp932 บนเอนจินนั้นต้องใช้ font hack ซึ่งยังไม่ได้ทำ
อักษรละตินเพี้ยน: mojibake
ข้อความญี่ปุ่นที่เขียนเป็น Shift-JIS แล้วถูกอ่านเป็น UTF-8 (หรือกลับกัน) ให้อักษรละตินที่มีเครื่องหมายกำกับและอักขระวาดกรอบยาวเป็นพืด อาการนี้โผล่มาเมื่อเครื่องมือเขียนไฟล์ด้วยการเข้ารหัสผิด หรือเมื่อตัวโหลดของเกมเองสมมติการเข้ารหัสที่การส่งออกของคุณไม่ได้ใช้ วิธีแก้คือเขียนไฟล์ด้วยการเข้ารหัสที่เอนจินคาดหวัง — ไม่ใช่การเปลี่ยนฟอนต์ ซึ่งไม่ทำให้อะไรเปลี่ยนเลย
ทำไม CJK และภาษาไทยจึงโดนหนักที่สุด
ภาษาละตินต้องการกลิฟราวหนึ่งร้อยตัว ภาษาญี่ปุ่นต้องการหลายพัน ภาษาจีนมากกว่านั้น ไฟล์ฟอนต์มีขนาดใหญ่ และเกมส่งฟอนต์ที่เล็กที่สุดเท่าที่ครอบคลุมสคริปต์ของตัวเองมา ความครอบคลุมที่คุณได้รับสืบทอดมาจึงขึ้นอยู่กับว่าเกมถูกสร้างมาเพื่อภาษาใดล้วน ๆ
- เกมที่สร้างมาเพื่อ ภาษาญี่ปุ่น มักมีคานะ คันจิที่ใช้บ่อย และ ASCII แปลเป็นรัสเซีย กรีก ไทย หรือเกาหลี แล้วอักขระที่ไม่ใช่ ASCII ทุกตัวจะหลุดออกไปหมด
- เกมที่สร้างมาเพื่อ ภาษาอังกฤษ มักมีแค่ละติน — ญี่ปุ่น จีน เกาหลี ไทย กรีก และซีริลลิกจึงพังพร้อมกันทั้งหมด
- ภาษาไทย เป็นกรณีที่แย่ที่สุดในทางปฏิบัติ: มันอยู่นอกฟอนต์เกมทั่วไปทุกตัว และยังต้องการสระและวรรณยุกต์ที่วางประกอบอยู่เหนือและใต้อักษรฐาน ฟอนต์ที่มีโค้ดพอยต์ครบในทางเทคนิคจึงยังเรนเดอร์ออกมาไม่ดีได้
- ภาษาละตินไม่ได้ปลอดภัยโดยอัตโนมัติ โปแลนด์ เช็ก ฮังการี ตุรกี เวียดนาม และโรมาเนียใช้อักขระ Latin-Extended ที่ฟอนต์ซึ่งสร้างมาเพื่ออังกฤษหรือญี่ปุ่นมักไม่มี RuneTranslate จึงรวมฟอนต์ที่กว้างกว่ามาให้สำหรับปลายทางเหล่านั้นบน Ren'Py เพราะ "ละตินไม่มีปัญหา" กลายเป็นเรื่องไม่จริง
เจาะจงที่ Unity และ TextMeshPro
Unity มีระบบข้อความสองระบบ และมันล้มเหลวคนละแบบ uGUI Text รุ่นเก่าใช้อ็อบเจกต์ Font ส่วน TextMeshPro — ซึ่งเกมสมัยใหม่ส่วนใหญ่ใช้ — ไม่ได้ใช้ไฟล์ฟอนต์ตอนรันไทม์เลย มันใช้ TMP font asset: แอตลาสเท็กซ์เจอร์ของภาพกลิฟที่เรนเดอร์ไว้ล่วงหน้า บวกตารางที่แมปโค้ดพอยต์ไปยังตำแหน่งในแอตลาสนั้น
นั่นคือเหตุผลที่ TMP font asset มี ชุดอักขระตายตัว ตอนที่นักพัฒนาสร้างมันขึ้นมา เขาเลือกช่วงอักขระไว้ — มักเป็นแค่ภาษาที่เขากำลังจะวางขาย — แล้ว TMP อบเฉพาะกลิฟเหล่านั้นลงแอตลาส font asset แบบ static ไม่มีทางเรนเดอร์อะไรที่อยู่นอกชุดนั้นได้เลย ไม่ว่าคุณจะป้อนอะไรให้มัน ส่วน font asset แบบ dynamic เก็บการอ้างอิงไปยังไฟล์ฟอนต์ต้นทางไว้และแรสเตอร์ไรซ์กลิฟใหม่ตามต้องการได้ ซึ่งเป็นโหมดเดียวที่ขยายไปครอบคลุมภาษาที่นักพัฒนาไม่เคยคิดถึงได้
ทางออกของ TMP คือ รายการฟอนต์สำรอง font asset แต่ละตัวมีรายการ m_fallbackFontAssets และ TMP Settings มีรายการสำรองระดับโกลบอล เมื่อแอสเซ็ตหลักไม่มีกลิฟสำหรับโค้ดพอยต์หนึ่ง TMP จะไล่ดูรายการเหล่านั้นเพื่อหาตัวที่มี นี่คือจุดที่ถูกต้องสำหรับการเข้าไปแทรก เพราะมันปล่อยให้ฟอนต์ของเกมเองคุมทุกอย่างที่มันวาดได้อยู่แล้ว — UI จึงคงหน้าตาตามที่ตั้งใจไว้ และมีเฉพาะอักขระที่มันรับมือไม่ได้เท่านั้นที่มาจากที่อื่น
แก้ด้วยเครื่องมือตอนรันไทม์
XUnity.AutoTranslator hook เกมขณะที่มันทำงานและสลับฟอนต์ได้จากไฟล์คอนฟิกของมัน การตั้งค่าที่เกี่ยวข้องอยู่ใต้ [Behaviour]:
OverrideFont— แทนที่ฟอนต์ที่คอมโพเนนต์Textของ Unity UI รุ่นเก่าใช้ รับชื่อฟอนต์ที่เกมหรือระบบปฏิบัติการหาเจอOverrideFontTextMeshPro— แทนที่ TMP font asset ทั้งตัว ชี้ไปที่ไฟล์ TMP font AssetBundle หรือชื่อแอสเซ็ตที่เกมมีอยู่แล้วFallbackFontTextMeshPro— ตัวที่ปลอดภัยกว่าในสองตัวนี้: เพิ่มฟอนต์ที่ TMP จะไปดูเฉพาะกลิฟที่แอสเซ็ตของเกมเองไม่มี โดยคงแบบอักษรเดิมไว้ทุกที่ที่เหลือ- tmp_font_assetbundles — คอลเลกชันที่ชุมชนดูแล ซึ่งรวม TMP font AssetBundle สำเร็จรูปไว้ และเป็นสิ่งที่
OverrideFontTextMeshProกับFallbackFontTextMeshProมักถูกชี้ไปหา บันเดิลเหล่านี้ถูกสร้างแยกตามเวอร์ชัน Unity และบันเดิลที่สร้างมาสำหรับ Unity คนละเวอร์ชันกับเกมจะโหลดไม่ขึ้น — การจับคู่เวอร์ชันคือขั้นที่คนมักพลาด
เครื่องมือตอนรันไทม์ผูกกับเครื่องและคอนฟิก: มันแก้เกมบนพีซีที่มันถูกติดตั้งไว้ และใครก็ตามที่รันบิลด์นั้นต้องตั้งค่าแบบเดียวกันเอง RuneTranslate อ่านและเขียนไฟล์ _AutoGeneratedTranslations.txt ของปลั๊กอินได้ โปรเจกต์ที่ใช้มันอยู่แล้วจึงไม่ใช่ทางตัน — ดู การเปรียบเทียบ ว่าแต่ละแนวทางเหมาะกับตรงไหน
แก้ที่ระดับไฟล์
RuneTranslate ใช้เส้นทางฟอนต์สำรองโดยตรงในข้อมูลของเกม ตอนส่งออก Unity มันหาตำแหน่ง TMP Settings ของเกม หา font asset ที่ใช้เป็นแม่แบบได้ และหาฟอนต์ แล้วสร้าง TMP font asset ตัวใหม่ในโหมด Dynamic รอบ ๆ ฟอนต์ที่รวมมาซึ่งครอบคลุมสคริปต์ปลายทาง จากนั้นต่อท้ายมันเข้าไปในรายการสำรอง รายการสำรองเดิมไม่ถูกแตะต้อง และฟอนต์ของเกมเองยังเป็นตัวหลัก — เกมญี่ปุ่นที่แปลเป็นไทยจึงคงสไตล์เดิมไว้สำหรับทุกอย่างที่เป็นละติน และดึงกลิฟไทยมาจากแอสเซ็ตที่ถูกฉีดเข้าไป
ข้อควรรู้ตามตรงสองข้อ มันต้องมี TMP Settings, แม่แบบ font asset ที่ใช้ได้ และ Font อยู่ในคอนเทนเนอร์เดียวกันของแอสเซ็ตเกม เกมที่จัดวางสิ่งเหล่านั้นต่างออกไปอาจอยู่นอกขอบเขต และเมื่อการฉีดถูกข้าม การส่งออกจะ บอกคุณ ในคำเตือน แทนที่จะจบแบบเขียวสวยงาม — การส่งออกที่ออกมาเป็นกล่องต้องไม่ดูเหมือนการส่งออกที่สำเร็จ ถ้าคุณเห็นคำเตือนนั้น เส้นทางเครื่องมือตอนรันไทม์ข้างบนคือแผนสำรอง
Ren'Py ได้ผลลัพธ์เดียวกันด้วยกลไกที่ต่างออกไป เพราะ Ren'Py ไม่มี TMP: RuneTranslate รวมฟอนต์ CJK หรือฟอนต์ที่ไม่ใช่ละตินเข้าไปในบิลด์ที่ส่งออกได้ แล้วลงทะเบียนผ่าน config.font_replacement_map ของ Ren'Py เอง เอนจินจึงแทนที่ฟอนต์ให้ทุกที่ที่ฟอนต์ของเกมจะเอาไม่อยู่
เช็กลิสต์สำหรับใช้งานจริง
- ดูความเสียหายก่อน สี่เหลี่ยมที่เหมือนกันหมด อักขระ
?หรือตัวอักษรเพี้ยน — ตัดสินให้ได้ว่าคุณเจอแบบไหนในสามแบบก่อนจะแตะอะไร - ถ้าเป็น
?ให้เปิดไฟล์ที่ส่งออก ถ้าเครื่องหมายคำถามอยู่ในไฟล์แล้ว อักขระหายไปตั้งแต่ตอนเขียนและไม่มีฟอนต์ไหนช่วยได้ ให้ตรวจว่าการเข้ารหัสสตริงของเอนจินรองรับภาษาปลายทางของคุณหรือไม่ - ถ้าเป็นตัวอักษรเพี้ยน ให้ตรวจการเข้ารหัสที่เอนจินคาดหวังสำหรับไฟล์นั้น แทนที่จะไปตรวจฟอนต์
- ถ้าเป็นกล่อง ให้ยืนยันว่าเนื้อหาในไฟล์ถูกต้อง — ซึ่งเกือบทุกครั้งก็ถูกอยู่แล้ว — แล้วจัดการมันในฐานะงานฟอนต์
- บน Unity ให้เลือกเพิ่มฟอนต์สำรองมากกว่าการแทนที่ฟอนต์ของเกม การแทนที่เปลี่ยนหน้าตาของทุกสตริง รวมถึงตัวที่เรนเดอร์ดีอยู่แล้ว
- ถ้าคุณใช้ TMP font AssetBundle ให้จับคู่มันกับเวอร์ชัน Unity ของเกม
- ส่งออกใหม่แล้วตรวจอักขระ ตัวที่มีปัญหาโดยเฉพาะ ไม่ใช่แค่บทสนทนาบรรทัดแรก อักขระ Latin-Extended และวรรณยุกต์ไทยมักผ่านการสุ่มตรวจแต่ไปพังที่อื่น
ไปต่อที่ไหน
สำหรับเวิร์กโฟลว์ Unity ฉบับเต็ม — ว่า Unity เปิดเผยข้อความอะไรบ้าง และอะไรที่ยังเอื้อมไม่ถึง — อ่าน วิธีแปลเกม Unity และ หน้าเอนจิน Unity ข้อความที่ถูกอบลงในงานศิลป์แทนที่จะอยู่ในข้อมูลเป็นคนละปัญหาและใช้คนละเครื่องมือ: ดู การแปลรูปภาพ ส่วน คำถามที่พบบ่อย ครอบคลุมเรื่องน่าประหลาดใจอื่น ๆ ในการใช้งานครั้งแรก
RuneTranslate ใช้ฟรีโดยปลดล็อกทุกเอนจินและทุกผู้ให้บริการ รวมถึงสามรายที่ไม่ต้องใช้คีย์ API เลย มันแยกวิเคราะห์ไฟล์ของเกมเองแล้วส่งออกบิลด์ที่แปลแล้วซึ่งเล่นได้และเป็นของคุณ ดาวน์โหลดเลย หรือดู เอนจินที่รองรับ
พร้อมลอง RuneTranslate ไหม
ระดับฟรีปลดล็อกทุกเอนจิน + ทุกผู้ให้บริการแปล Supporter ($3/mo) ปลดล็อกความเร็วเต็ม
ดาวน์โหลดสำหรับ Windows
