3 นาที

สถาปัตยกรรมสากลสำหรับแอปที่สร้างจากแชท

สถาปัตยกรรมสากลสำหรับแอปที่สร้างจากแชท: กำหนดคีย์สตริงที่มั่นคง กฎพหุ และเวิร์กโฟลว์การแปลชุดเดียวที่คงที่ทั้งเว็บและมือถือ

สถาปัตยกรรมสากลสำหรับแอปที่สร้างจากแชท

สิ่งที่พังก่อนเมื่อคุณเพิ่มภาษามากขึ้น

สิ่งแรกที่พังไม่ใช่โค้ด แต่เป็นคำพูด

แอปที่สร้างจากแชทมักเริ่มจากโปรโตไทป์ที่เร็ว: คุณพิมพ์ "เพิ่มปุ่มที่เขียนว่า บันทึก" UI ปรากฏ แล้วคุณก็ไปต่อ แต่สักพักคุณอยากได้สเปนและเยอรมัน แล้วพบว่าป้าย "ชั่วคราว" เหล่านั้นกระจัดกระจายอยู่ทั่วหน้าจอ คอมโพเนนต์ อีเมล และข้อความข้อผิดพลาด

การเปลี่ยนคำก็เกิดบ่อยกว่าการเปลี่ยนโค้ดด้วย ชื่อผลิตภัณฑ์ถูกเปลี่ยน ข้อความทางกฎหมายแก้ไข การแนะนำถูกเขียนใหม่ และฝ่ายซัพพอร์ตขอข้อความข้อผิดพลาดที่ชัดเจนขึ้น ถ้าข้อความฝังอยู่ในโค้ด UI โดยตรง การเปลี่ยนคำเพียงเล็กน้อยจะกลายเป็นการปล่อยที่มีความเสี่ยง และคุณจะพลาดจุดที่แนวความคิดเดียวกันถูกถ่ายทอดต่างกัน

สัญญาณเริ่มต้นที่บอกว่าคุณกำลังก่อ "หนี้การแปล":

  • ผสมหลายภาษาในหน้าจอเดียว (สตริงบางส่วนถูกแปล บางส่วนยังเป็นภาษาอังกฤษ)
  • ป้ายเดียวกันซ้ำกันหลายจุดแต่ต่างกันเล็กน้อย ("ลงทะเบียน", "Sign Up", "สร้างบัญชี")
  • เลย์เอาต์แตกเมื่อข้อความยาวขึ้น (ปุ่มล้น หัวเรื่องขึ้นบรรทัดใหม่ไม่สวย)
  • เวอร์ชันมือถือและเว็บเริ่มเบี่ยง (คำต่างกันสำหรับการกระทำเดียวกัน)
  • เนื้อหาซัพพอร์ตและข้อความระบบตาม UI ไม่ทัน

ตัวอย่างสมจริง: คุณสร้าง CRM ง่ายๆ ใน Koder.ai เว็บบอกว่า "Deal stage" แอปมือถือบอกว่า "Pipeline step" และ toast ข้อผิดพลาดบอกว่า "Invalid status" ถึงแม้ทั้งสามจะถูกแปล ผู้ใช้ก็จะรู้สึกว่าแอปไม่สอดคล้องกันเพราะแนวความคิดไม่ตรงกัน

"สอดคล้อง" ไม่ได้หมายถึง "ตัวอักษรเหมือนกันทุกที่" แต่มันหมายถึง:

  • แนวความคิดเดียวกันใช้คีย์และความหมายเดียวกันทั่วทั้งหน้าจอ
  • เว็บและมือถือใช้แหล่งความจริงสำหรับการแปลแหล่งเดียว
  • น้ำเสียงและศัพท์เทคนิคคงที่ (ทางการ vs เป็นมิตร, "customer" vs "client")
  • เลย์เอาต์ออกแบบรับมือประโยคที่ยาวสั้นได้

เมื่อคุณถือว่าข้อความเป็นข้อมูลผลิตภัณฑ์ ไม่ใช่เครื่องประดับ การเพิ่มภาษาจะไม่ใช่การรีบทำอีกต่อไป แต่เป็นส่วนปกติของการพัฒนา

แนวคิดพื้นฐานและเป้าหมายง่ายๆ ที่ควรมุ่งหวัง

Internationalization (i18n) คือการทำงานให้แอปรองรับหลายภาษาโดยไม่ต้องเขียนใหม่ Localization (l10n) คือเนื้อหาที่เหมาะกับภาษาหรือภูมิภาค เช่น ภาษาฝรั่งเศส (แคนาดา) พร้อมคำ วันเดือนปี และน้ำเสียงที่ถูกต้อง

เป้าหมายง่ายๆ: ข้อความที่ผู้ใช้เห็นทุกชิ้นถูกเลือกโดยคีย์ที่มั่นคง ไม่ได้พิมพ์ตรงในโค้ด UI ถ้าคุณสามารถเปลี่ยนประโยคโดยไม่ต้องเปิดคอมโพเนนต์ React หรือวิดเจ็ต Flutter แสดงว่าคุณมาถูกทางแล้ว นี่คือหัวใจของสถาปัตยกรรม i18n สำหรับแอปที่สร้างจากแชท เพราะง่ายจะเผลอส่งข้อความที่เขียนทับในแชทไปเป็นโค้ด

ข้อความที่ผู้ใช้เห็นกว้างกว่าที่หลายทีมคิด รวมทั้งปุ่ม ป้าย ข้อผิดพลาดการตรวจสอบ ปุ่มสถานะว่าง คำแนะนำการเริ่มต้น การแจ้งเตือน push อีเมล การส่งออกเป็น PDF และข้อความใดๆ ที่ผู้ใช้เห็นหรือได้ยิน โดยปกติไม่รวมล็อกภายใน ชื่อคอลัมน์ฐานข้อมูล รหัสเหตุการณ์วิเคราะห์ ฟีเจอร์แฟลก หรืองานดีบักสำหรับผู้ดูแลเท่านั้น

ควรเก็บการแปลไว้ที่ไหน? ในทางปฏิบัติมักอยู่ทั้ง frontend และ backend โดยมีขอบเขตที่ชัดเจน

  • Frontend รับผิดชอบ UI chrome: การนำทาง ฟอร์ม เมนู และข้อความหน้าจอส่วนใหญ่
  • Backend รับผิดชอบข้อความที่มันสร้าง: อีเมลธุรกรรม การตรวจสอบฝั่งเซิร์ฟเวอร์ และสิ่งที่ส่งกลับเป็นการตอบข้อผิดพลาด
  • โดเมนที่ใช้ร่วมกัน (เช่น ป้ายสถานะคำสั่งซื้อ) ควรมาจากแหล่งความจริงเดียว เพื่อไม่ให้เว็บและมือถือเบี่ยงกัน

ข้อผิดพลาดที่ต้องหลีกเลี่ยงคือการผสมความรับผิดชอบ หาก backend คืนประโยคภาษาอังกฤษเต็มรูปสำหรับข้อผิดพลาด UI ฝั่ง frontend จะไม่สามารถทำการท้องถิ่นได้สะอาดกว่า แบบที่ดีกว่าคือ: backend คืนรหัสข้อผิดพลาด (และพารามิเตอร์ที่ปลอดภัยได้) แล้วไคลเอนต์แม็ปรหัสนั้นไปเป็นข้อความแปล

ความเป็นเจ้าของคำคือการตัดสินใจด้านผลิตภัณฑ์ ไม่ใช่รายละเอียดทางเทคนิค ตัดสินใจแต่แรกว่าใครสามารถเปลี่ยนคำและอนุมัติโทนได้

ถ้าผลิตภัณฑ์เป็นเจ้าของคำ ให้ปฏิบัติต่อการแปลเหมือนเนื้อหา: เวอร์ชัน ตรวจทาน และให้วิธีปลอดภัยให้ผลิตภัณฑ์ขอเปลี่ยน ถ้าเอนจินีเนียริ่งเป็นเจ้าของคำ ให้ตั้งกฎว่าทุกสตริง UI ใหม่ต้องมาพร้อมคีย์และการแปลเริ่มต้นก่อนปล่อย

ตัวอย่าง: ถ้ากระบวนการสมัครของคุณเขียนว่า "Create account" ในสามหน้าจอ ให้ทำเป็นคีย์เดียวที่ใช้ทั่ว นั่นช่วยให้ความหมายสอดคล้อง ทำให้คนแปลทำงานเร็วขึ้น และป้องกันการเปลี่ยนสำนวนเล็กน้อยกลายเป็นการทำความสะอาดหลายหน้าจอภายหลัง

จะจัดโครงสร้างคีย์อย่างไรให้คงที่

คีย์คือสัญญาระหว่าง UI ของคุณกับการแปล หากสัญญานี้เปลี่ยนบ่อย คุณจะได้ข้อความหาย การแก้ด่วน และคำที่ไม่สอดคล้องกันระหว่างเว็บและมือถือ สถาปัตยกรรม i18n ที่ดีสำหรับแอปที่สร้างจากแชทเริ่มจากกฎเดียว: คีย์ควรบรรยายความหมาย ไม่ใช่ประโยคภาษาอังกฤษปัจจุบัน

ใช้ ID ที่มั่นคงเป็นคีย์ (เช่น billing.invoice.payNow) แทนการใช้สำเนาข้อความทั้งหมด (เช่น "Pay now") คีย์ที่เป็นประโยคจะพังทันทีเมื่อใครสักคนปรับคำ เพิ่มเครื่องหมายวรรคตอน หรือเปลี่ยนตัวพิมพ์

รูปแบบปฏิบัติที่ยังอ่านง่ายคือ: หน้าจอ (หรือโดเมน) + คอมโพเนนต์ + เจตนา เก็บให้เรียบและคาดเดาได้

ตัวอย่าง:

  • auth.login.title
  • auth.login.emailLabel
  • billing.checkout.payButton
  • nav.settings
  • errors.network.offline

ตัดสินใจว่าจะใช้คีย์เดิมหรือสร้างคีย์ใหม่โดยถามว่า: "ความหมายเหมือนกันในทุกที่ไหม?" ให้ใช้คีย์ซ้ำสำหรับการกระทำที่เป็นกลางจริงๆ แต่แยกคีย์เมื่อบริบทเปลี่ยน เช่น "บันทึก" ในหน้าการแก้ไขโปรไฟล์อาจเป็นการกระทำง่ายๆ ขณะที่ "บันทึก" ในตัวแก้ไขซับซ้อนอาจต้องน้ำเสียงเฉพาะในบางภาษา

เก็บข้อความ UI ร่วมกันไว้ใน namespace เฉพาะเพื่อไม่ให้ซ้ำกันในหลายหน้าจอ ถังทั่วไปที่ใช้งานได้ดี เช่น:

  • common.actions.* (save, cancel, delete)
  • common.status.* (loading, success)
  • common.fields.* (search, password)
  • errors.* (validation, network)
  • nav.* (tabs, menu items)

เมื่อคำเปลี่ยนแต่ความหมายไม่เปลี่ยน ให้คงคีย์ไว้และอัปเดตค่าการแปลเท่านั้น นี่คือจุดประสงค์ของ ID ที่มั่นคง หากความหมายเปลี่ยน (แม้เพียงเล็กน้อย) ให้สร้างคีย์ใหม่และเก็บของเก่าไว้จนกว่าจะมั่นใจว่าไม่ได้ใช้อีก วิธีนี้ป้องกันการไม่ตรงกันแบบเงียบๆ ที่การแปลเก่าดูเหมือนมีแต่ผิด

ตัวอย่างเล็กๆ จากกระแส Koder.ai: แชทของคุณสร้างทั้งเว็บ React และมือถือ Flutter ถ้าทั้งคู่ใช้ common.actions.save คุณจะได้การแปลที่สอดคล้อง แต่ถ้าเว็บใช้ profile.save และมือถือใช้ account.saveButton คุณจะเริ่มเบี่ยงเมื่อเวลาผ่านไป แม้ภาษาอังกฤษจะเหมือนกันตอนนี้

ข้อควรเก็บสตริงไว้ที่ไหนและจัดไฟล์การแปลอย่างไร

ถือว่าภาษาแหล่ง (มักเป็นภาษาอังกฤษ) เป็นแหล่งความจริงเดียว เก็บไว้ที่เดียว ตรวจทานเหมือนโค้ด และหลีกเลี่ยงการปล่อยให้สตริงโผล่ในคอมโพเนนต์ต่างๆ แบบ "เดี๋ยวค่อยแก้" นี่เป็นวิธีที่เร็วที่สุดในการหลีกเลี่ยงข้อความ UI ฮาร์ดโค้ดและการทำงานซ้ำในภายหลัง

กฎง่ายๆ ช่วยได้: แอปอาจแสดงข้อความจากระบบ i18n เท่านั้น ถ้าต้องการข้อความใหม่ ให้เพิ่มคีย์และข้อความเริ่มต้นก่อน แล้วใช้คีย์นั้นใน UI วิธีนี้รักษาโครงสร้าง i18n ของแอปที่สร้างจากแชทให้คงที่แม้ฟีเจอร์จะย้ายที่

โครงแฟ้มที่ยังคงสมเหตุสมผล

ถ้าคุณปล่อยทั้งเว็บและมือถือ คุณต้องการแค็ตตาล็อกคีย์ที่ใช้ร่วมกันหนึ่งชุด พร้อมพื้นที่ให้ทีมฟีเจอร์ทำงานโดยไม่ชนกัน รูปแบบปฏิบัติหนึ่งชุด:

  • /i18n
  • /i18n/locales/en.json (ภาษาแหล่ง)
  • /i18n/locales/es.json, /i18n/locales/fr.json, ...
  • /i18n/features/billing.json, /i18n/features/auth.json, ...
  • /i18n/shared.json (ปุ่ม ป้ายทั่วไป ข้อผิดพลาด)

เก็บคีย์ให้เหมือนกันข้ามแพลตฟอร์ม แม้ว่าการใช้งานจะแตกต่างกัน (React บนเว็บ, Flutter บนมือถือ) หากคุณใช้แพลตฟอร์มอย่าง Koder.ai เพื่อสร้างทั้งสอง แอ็กซปอร์ตซอร์สโค้ดจะง่ายขึ้นเมื่องานทั้งสองชี้ไปยังคีย์และรูปแบบข้อความเดียวกัน

การเวอร์ชันและการตรวจทานที่ป้องกันหนี้การแปล

การแปลเปลี่ยนตามเวลา ปฏิบัติต่อการเปลี่ยนแปลงเหมือนการเปลี่ยนผลิตภัณฑ์: เล็ก ตรวจทานได้ และติดตามได้ การตรวจทานที่ดีเน้นความหมายและการนำกลับมาใช้ ไม่ใช่แค่การสะกด

  • ต้องมีการรีวิว PR สำหรับการเปลี่ยนแปลงในภาษาแหล่ง
  • ห้ามลบคีย์โดยไม่ค้นหาและวางแผน
  • เพิ่มบันทึกสำหรับนักแปลเมื่อข้อความกำกวม
  • ใช้การตรวจ "การแปลขาดหาย" ใน CI แบบง่าย

เพื่อป้องกันคีย์ไหลระหว่างทีม ให้ถือคีย์เป็นของฟีเจอร์ (billing., auth.) และอย่าเปลี่ยนชื่อคีย์เพียงเพราะคำเปลี่ยน อัปเดตข้อความ คงคีย์ไว้ คีย์คือรหัสระบุ ไม่ใช่สำเนาข้อความ

การพจน์พหุและไวยากรณ์โดยไม่ต้องแฮ็ก

เปิดตัว locales ทีละฟีเจอร์
เพิ่มภาษาเป็นลำดับฟีเจอร์ทีละส่วนและควบคุมคีย์ที่ขาดหายให้เป็นศูนย์

กฎพหุเปลี่ยนตามภาษา ดังนั้นรูปแบบง่ายของภาษาอังกฤษ (1 กับ อื่นๆ) จะพังเร็ว บางภาษามีรูปสำหรับ 0, 1, 2-4 และอื่นๆ อีกมาก บางภาษาเปลี่ยนทั้งประโยคไม่ใช่แค่คำนาม หากคุณอัดตรรกะพหุไว้ใน UI ด้วย if-else คุณจะทำสำเนาข้อความและพลาดกรณีขอบ

วิธีที่ปลอดภัยคือเก็บข้อความยืดหยุ่นหนึ่งชิ้นต่อแนวคิดและปล่อยให้ชั้น i18n เลือกรูปแบบที่ถูกต้อง ข้อความสไตล์ ICU ถูกสร้างมาสำหรับเรื่องนี้ มันเก็บการตัดสินใจด้านไวยากรณ์ไว้ในการแปล ไม่ใช่ในคอมโพเนนต์ของคุณ

นี่คือตัวอย่างเล็กๆ ที่คลุมเคสที่คนมักลืม:

itemsCount = "{count, plural, =0 {No items} one {# item} other {# items}}"

คีย์เดียวครอบคลุม 0, 1 และที่เหลือ นักแปลสามารถแทนที่ด้วยรูปพหุที่ถูกต้องสำหรับภาษาของตนโดยที่คุณไม่ต้องแตะโค้ด

เมื่อคุณต้องการคำพูดตามเพศหรือบทบาท หลีกเลี่ยงการสร้างคีย์แยกเช่น welcome_male และ welcome_female เว้นแต่ผลิตภัณฑ์จะต้องการจริงๆ ใช้ select เพื่อให้ประโยคเป็นหน่วยเดียว:

welcomeUser = "{gender, select, female {Welcome, Ms. {name}} male {Welcome, Mr. {name}} other {Welcome, {name}}}"

เพื่อหลีกเลี่ยงการติดกับกรณีไวยากรณ์ เก็บประโยคให้สมบูรณ์ อย่าเย็บชิ้นส่วนเช่น "{count} " + t('items') เพราะหลายภาษาไม่สามารถสลับคำแบบนั้นได้ เพลิดเพลินกับข้อความเดียวที่รวมตัวเลข คำนาม และคำรอบๆ

กฎง่ายๆ ที่ใช้ได้ดีในแอปสร้างจากแชท (รวมโปรเจกต์ Koder.ai) คือ: ถ้าประโยคมีตัวเลข บุคคล หรือสถานะ ให้ทำเป็น ICU ตั้งแต่วันแรก มันใช้ต้นทุนเพิ่มเล็กน้อยแต่ช่วยลดหนี้การแปลได้มาก

รักษาการแปลบนเว็บและมือถือให้สอดคล้อง

ถ้าเว็บ React และมือถือ Flutter ต่างเก็บไฟล์การแปลของตัวเอง พวกมันจะเบี่ยง คำเดียวกันกลายเป็นคำต่างกัน คีย์หนึ่งหมายถึงสิ่งหนึ่งบนเว็บและอีกอย่างบนมือถือ และตั๋วซัพพอร์ตจะเริ่มพูดว่า "แอปบอก X แต่เว็บไซต์บอก Y"

วิธีแก้ง่ายที่สุดและสำคัญที่สุด: เลือกรูปแบบแหล่งความจริงเดียวและปฏิบัติต่อมันเหมือนโค้ด สำหรับทีมส่วนใหญ่ นั่นหมายถึงชุดไฟล์ locale ที่ใช้ร่วมกัน (เช่น JSON ที่ใช้ข้อความสไตล์ ICU) ที่ทั้งเว็บและมือถือใช้งาน เมื่อคุณสร้างแอปผ่านแชทและตัวสร้าง นี่จะสำคัญขึ้นเพราะง่ายที่จะเผลอสร้างข้อความใหม่สองที่

แหล่งความจริงเดียว

การตั้งค่าปฏิบัติได้แก่แพ็กเกจ "i18n" เล็กๆ หรือโฟลเดอร์ที่ประกอบด้วย:

  • ไฟล์ locale สำหรับแต่ละภาษา (คีย์เหมือนกันทุกที่)
  • กฎรูปแบบข้อความ (ICU สำหรับพหุและช่องว่าง)
  • README สั้นๆ อธิบายวิธีเพิ่มคีย์

React และ Flutter เป็นผู้บริโภค พวกเขาไม่ควรคิดคีย์ใหม่ท้องถิ่น ในเวิร์กโฟลว์แบบ Koder.ai (เว็บ React, มือถือ Flutter) คุณสามารถสร้างไคลเอนต์ทั้งสองจากชุดคีย์เดียว และเก็บการเปลี่ยนแปลงภายใต้การตรวจสอบเหมือนการเปลี่ยนโค้ดอื่นๆ

การสอดคล้องกับ backend ก็เป็นส่วนหนึ่งของเรื่องเดียวกัน ข้อผิดพลาด การแจ้งเตือน และอีเมลไม่ควรเป็นประโยคภาษาอังกฤษที่เขียนด้วยมือใน Go แต่ให้คืนรหัสข้อผิดพลาดที่มั่นคง (เช่น auth.invalid_password) พร้อมพารามิเตอร์ที่ปลอดภัย แล้วไคลเอนต์แม็ปรหัสไปเป็นข้อความแปล สำหรับอีเมลที่ส่งจากเซิร์ฟเวอร์ เซิร์ฟเวอร์สามารถเรนเดอร์เทมเพลตโดยใช้คีย์และไฟล์ locale เดียวกัน

กฎที่ช่วยให้คุณสอดคล้อง

สร้างสมุดกฎเล็กๆ และบังคับใช้ในการตรวจโค้ด:

  • ข้อความ UI ใหม่ต้องมีคีย์ในไฟล์ locale ที่แชร์ก่อน
  • คีย์ต้องมี namespace และเจตนาชัดเจน (ไม่ใช่ตำแหน่งหน้าจอ)
  • ทุกคีย์ต้องกำหนด placeholder หนึ่งครั้งและใช้เหมือนกันทุกที่
  • ถ้าสองวลีมีความหมายต่างกัน ห้ามใช้คีย์เดียวกัน
  • ถ้าคีย์สองอันมีความหมายเดียวกัน ให้ลบอันหนึ่งและเลือกอันเดียว

เพื่อป้องกันคีย์ซ้ำที่มีความหมายต่างกัน ให้เพิ่มฟิลด์ "description" (หรือไฟล์คอมเมนต์) สำหรับนักแปลและตัวคุณในอนาคต ตัวอย่าง: billing.trial_days_left ควรอธิบายว่าจะแสดงเป็นแบนเนอร์ อีเมล หรือทั้งสอง ประโยคเดียวมักหยุดการใช้ซ้ำแบบ "พอได้" ที่สร้างหนี้การแปล

ความสม่ำเสมอนี้คือกระดูกสันหลังของสถาปัตยกรรม i18n สำหรับแอปที่สร้างจากแชท: พจนานุกรมร่วมหนึ่งชุด หลายพื้นผิว และไม่มีเรื่องเซอร์ไพรส์เมื่อคุณปล่อยภาษาถัดไป

ขั้นตอนทีละขั้นที่ใช้ได้กับโปรเจกต์จริง

รับรางวัลขณะสร้าง
รับเครดิตโดยการสร้างเนื้อหาเกี่ยวกับการสร้างของคุณหรือแนะนำผู้อื่นให้กับ Koder.ai

สถาปัตยกรรม i18n ที่ดีเริ่มง่าย: ชุดคีย์ข้อความหนึ่งชุด แหล่งความจริงสำหรับคำ และกฎเดียวกันบนเว็บและมือถือ หากคุณพัฒนาเร็ว (เช่น ด้วย Koder.ai) โครงสร้างนี้ช่วยให้ความเร็วโดยไม่สร้างหนี้การแปล

การตั้งค่าปฏิบัติ (เว็บ + มือถือ)

เลือก locale ตั้งแต่ต้นและตัดสินใจว่าจะทำอย่างไรเมื่อการแปลขาด วิธีปกติคือ: แสดงภาษาที่ผู้ใช้ต้องการเมื่อมี มิฉะนั้น fallback เป็นภาษาอังกฤษ และบันทึกคีย์ที่ขาดเพื่อแก้ก่อนปล่อย

จากนั้นทำตามนี้:

  • กำหนดภาษาที่รองรับและ fallback: ตัดสินใจภาษาที่รองรับ ภาษาเริ่มต้น และลำดับ fallback ชัดเจน และตกลงวิธีตรวจจับ locale (การตั้งค่าเบราว์เซอร์/แอป หรือโปรไฟล์ผู้ใช้)
  • สร้างฟังก์ชันการแปลและแนวทางตั้งชื่อคีย์: ใช้คีย์ที่มั่นคงตามความหมาย (ไม่ใช่ประโยคเต็ม) เช่น: billing.plan_name.pro หรือ auth.error.invalid_password ใช้คีย์เดียวกันทุกที่
  • เชื่อมต่อกับ React และ Flutter: ใน React ห่อแอปด้วย i18n provider และใช้ t("key") ในคอมโพเนนต์ ใน Flutter ใช้ตัวห่อ localization และเรียก lookup โดยใช้คีย์เดียวกันในวิดเจ็ต เป้าหมายคือคีย์เดียวกัน ไม่จำเป็นต้องเป็นไลบรารีเดียวกัน
  • รองรับตัวแปรและกฎพหุตั้งแต่วันแรก: ใช้ข้อความสไตล์ ICU สำหรับพหุและ placeholder เช่น “{count, plural, one {# file} other {# files}}” และ “Hello, {name}” เพื่อลดการใช้ if(count === 1) กระจัดกระจาย
  • เพิ่มขั้นตอนตรวจทวนคำเบาๆ: ก่อนปล่อย ตรวจสอบสตริงใหม่หรือเปลี่ยน: ตรวจชื่อนิยามคีย์ ลบข้อความฮาร์ดโค้ด ยืนยัน placeholder ตรงกัน และตรวจว่าทั้งเว็บและมือถือรับการเปลี่ยนหรือไม่

สุดท้าย ทดสอบภาษาหนึ่งที่คำมักยาวกว่า (เยอรมันเป็นตัวอย่าง) และอีกภาษาที่มีเครื่องหมายวรรคตอนต่างกัน วิธีนี้จะเผยปัญหาปุ่มล้น หัวเรื่องขึ้นบรรทัดใหม่ไม่สวย และเลย์เอาต์ที่สมมติภาษาอังกฤษเร็ว

ถ้าคุณเก็บการแปลในโฟลเดอร์แชร์ (หรือแพ็กเกจที่สร้าง) และถือว่าการเปลี่ยนคำเป็นการเปลี่ยนโค้ด เว็บและมือถือของคุณจะคงสอดคล้องแม้ว่าฟีเจอร์จะสร้างอย่างรวดเร็วในแชท

เนื้อหาไดนามิก: วันที่ ตัวเลข และข้อความผู้ใช้

ข้อความ UI ที่แปลแล้วเป็นเพียงครึ่งหนึ่งของเรื่อง ส่วนใหญ่แอปยังแสดงค่าที่เปลี่ยน เช่น วันที่ ราคา จำนวน และชื่อ หากคุณถือค่านั้นเป็นข้อความธรรมดา คุณจะได้รูปแบบแปลก เวลาโซนผิด และประโยคที่ฟังดูไม่ถูกต้องในหลายภาษา

เริ่มจากการฟอร์แมตราคา ตัวเลข และวันที่ตามกฎ locale ไม่ใช่โค้ดกำหนดเอง ผู้ใช้ในฝรั่งเศสคาดว่า "1 234,50 €" ขณะที่ผู้ใช้ในสหรัฐคาด "$1,234.50" ข้อเดียวกันใช้กับวันที่: "03/04/2026" กำกวม แต่การฟอร์แมตตาม locale ทำให้ชัดเจน

โซนเวลาเป็นกับดักถัดไป เซิร์ฟเวอร์ควรเก็บ timestamp ในรูปเป็นกลาง (ปกติ UTC) แต่ผู้ใช้คาดว่าจะเห็นเวลาในโซนของตน ตัวอย่าง: คำสั่งสร้างเวลา 23:30 UTC อาจเป็น "พรุ่งนี้" สำหรับคนในโตเกียว ตัดสินกฎต่อหน้าจอ: แสดงเวลาตามผู้ใช้สำหรับเหตุการณ์ส่วนบุคคล และแสดงโซนธุรกิจคงที่สำหรับหน้าที่เช่นเวลารับสินค้า (และติดป้ายให้ชัด)

หลีกเลี่ยงการต่อประโยคด้วยการประกอบชิ้นส่วนที่แปลแล้ว มันทำลายไวยากรณ์เพราะลำดับคำเปลี่ยน เช่นแทนที่จะทำ:

"{count} " + t("items") + " " + t("in_cart")

ให้ใช้ข้อความเดียวที่มี placeholder เช่น: "{count} items in your cart" นักแปลจะสามารถสลับคำได้อย่างปลอดภัย

ภาษาเขียนจากขวาไปซ้าย (RTL)

RTL ไม่ใช่แค่ทิศทางข้อความ การไหลของเลย์เอาต์พลิก บางไอคอนต้องกลับด้าน (เช่น ลูกศรกลับหลัง) และเนื้อหาผสม (อาหรับกับรหัสผลิตภัณฑ์ภาษาอังกฤษ) อาจเรนเดอร์ลำดับแปลก ทดสอบหน้าจอจริง ไม่ใช่แค่ป้ายเดียว และแน่ใจว่าส่วนประกอบ UI รองรับการเปลี่ยนทิศทาง

ข้อความที่ผู้ใช้สร้าง

อย่าแปลสิ่งที่ผู้ใช้เขียน (ชื่อ ที่อยู่ ตั๋วซัพพอร์ต ข้อความแชท) คุณสามารถแปลป้ายรอบๆ และฟอร์แมตเมตาดาต้า (วันที่ ตัวเลข) แต่เนื้อหาเองต้องคงไว้ หากคุณเพิ่มการแปลอัตโนมัติภายหลัง ให้เป็นฟีเจอร์ชัดเจนที่มีสลับ "ต้นฉบับ/แปล"

ตัวอย่างปฏิบัติ: แอปที่สร้างด้วย Koder.ai อาจแสดง "{name} renewed on {date} for {amount}" เก็บเป็นข้อความเดียว ฟอร์แมต {date} และ {amount} ตาม locale และแสดงในโซนเวลาผู้ใช้ รูปแบบเดียวกันป้องกันหนี้การแปลได้มาก

กฎด่วนที่มักป้องกันบั๊ก:

  • เก็บ timestamp เป็น UTC ฟอร์แมตตาม locale และโซนเวลาผู้ดู
  • ใช้ placeholder ในประโยคเต็ม ไม่ใช่ชิ้นส่วนต่อกัน
  • ฟอร์แมตสกุลเงินตามกฎ locale และรหัสสกุลเงินที่ถูกต้อง
  • ทดสอบอย่างน้อยหนึ่ง locale แบบ RTL บนหน้าจอจริง
  • ห้ามแปลข้อความที่ผู้ใช้สร้างเป็นค่าเริ่มต้น

ความผิดพลาดทั่วไปที่สร้างหนี้การแปล

วางแอปของคุณบนโดเมนของคุณ
ปล่อยแอปแปลภายใต้โดเมนที่กำหนดเองเมื่อพร้อมแชร์

หนี้การแปลมักเริ่มจาก "สตริงด่วนเดียว" แล้วกลายเป็นสัปดาห์ของการแก้ไข ในโปรเจกต์ที่สร้างจากแชท มันเกิดเร็วขึ้นเพราะข้อความ UI ถูกสร้างในคอมโพเนนต์ ฟอร์ม และแม้แต่ข้อความ backend

ปัญหาที่ทำให้เจ็บปวดในภายหลัง

ปัญหาที่แพงที่สุดคือสิ่งที่กระจายทั่วแอปและยากจะค้นหา

  • ฮาร์ดโค้ดข้อความในคอมโพเนนต์ UI รวม placeholder สถานะว่าง และป้ายปุ่ม คุณไม่สามารถตรวจสอบหรือใช้ซ้ำได้ และการเปลี่ยนคำเล็กน้อยต้องแก้โค้ด
  • ให้ backend คืนประโยคภาษาอังกฤษสำหรับการตรวจสอบและข้อผิดพลาด API UI จะแสดงหลายภาษาและคุณไม่สามารถแม็ปข้อผิดพลาดไปยังข้อความแปลได้อย่างเชื่อถือ
  • ใช้ประโยคภาษาอังกฤษเต็มเป็นคีย์การแปล รู้สึกสะดวกจนกระทั่งคำเปลี่ยน แล้วคีย์ก็สับเปลี่ยน คีย์เก่าค้างอยู่ และคุณเสียหน่วยความจำการแปล
  • คัดลอกคีย์ระหว่างเว็บและมือถือแล้วแก้ต่างกัน เมื่อเวลาผ่านไป หน้าจอ "เหมือนกัน" จะเบี่ยงและผู้ใช้จะสังเกตคำไม่สอดคล้องกัน
  • ผัดกฎพหุไว้จนกว่าจะเพิ่มภาษาแรกที่ไม่ใช่ภาษาอังกฤษ แล้วจะพบบั๊ก "1 items" จำนวนมากและไวยากรณ์แปลกๆ ที่แก้ไม่ได้ด้วย if-statements ง่ายๆ

ตัวอย่างสั้นๆ (เป็นอย่างไรในชีวิตจริง)

สมมติเว็บ React และมือถือ Flutter แสดงแบนเนอร์บิลลิ่งว่า: "You have 1 free credit left" ใครสักคนแก้เว็บเป็น "You have one credit remaining" และเก็บคีย์เป็นประโยคทั้งประโยค มือถือตายังใช้คีย์เก่า ตอนนี้คุณมีคีย์สองอันสำหรับแนวคิดเดียวกันและนักแปลจะเห็นทั้งสอง

แพทเทิร์นที่ดีกว่าคือคีย์ที่มั่นคง (เช่น billing.creditsRemaining) และการพจน์พหุด้วยข้อความสไตล์ ICU เพื่อให้ไวยากรณ์ถูกต้องในทุกภาษา ถ้าคุณใช้เครื่องมือสร้างบรรยากาศอย่าง Koder.ai ให้เพิ่มกฎตั้งแต่ต้น: ข้อความที่ผู้ใช้เห็นที่สร้างในแชทต้องลงในไฟล์แปล ไม่ใช่ภายในคอมโพเนนต์หรือข้อผิดพลาดของเซิร์ฟเวอร์ นิสัยเล็กๆ นี้จะปกป้องสถาปัตยกรรม i18n ของแอปที่สร้างจากแชทเมื่อโปรเจกต์เติบโต

เช็คลิสต์ด่วน ตัวอย่างจริง และขั้นตอนต่อไป

เมื่อ i18n รู้สึกยุ่งเหยิง มักเป็นเพราะพื้นฐานไม่เคยถูกเขียนลง สมุดเช็คลิสต์เล็กๆ และตัวอย่างหนึ่งจะช่วยทีมคุณ (และคุณในอนาคต) พ้นจากหนี้การแปล

เช็คลิสต์ด่วนสำหรับทุกหน้าจอใหม่:

  • คีย์มั่นคง: คีย์ต่อความหมาย ไม่ใช่ต่อคำ (ตัวอย่าง: billing.invoice.paidStatus ไม่ใช่ billing.greenLabel)
  • Fallbacks: กำหนดภาษาพื้นฐานชัดเจน และตัดสินใจว่าจะทำอย่างไรเมื่อคีย์ขาด (แสดงข้อความ fallback, บันทึก, หรือบล็อคการปล่อย)
  • กฎพหุ: ใช้ข้อความสไตล์ ICU สำหรับการนับ (0, 1, หลาย) แทนการใช้แฮ็กสตริง
  • การฟอร์แมต: ฟอร์แมตวันที่ เงิน และตัวเลขตาม locale (และแยกสกุลเงินจากภาษา)
  • ตรวจ RTL: ทดสอบอย่างน้อยหนึ่งภาษาอ่านจากขวาไปซ้ายแต่เนิ่นๆ เพื่อให้ปัญหาเลย์เอาต์ปรากฏก่อนจะมีหน้าจอ 200 หน้าจอ

ตัวอย่างง่าย: คุณกำลังปล่อยหน้าบิลลิ่งเป็นภาษาอังกฤษ สเปน และญี่ปุ่น UI มี: "Invoice", "Paid", "Due in 3 days", "1 payment method" / "2 payment methods", และยอดรวมเช่น "$1,234.50" ถ้าคุณสร้างสิ่งนี้ด้วยสถาปัตยกรรม i18n สำหรับแอปที่สร้างจากแชท คุณกำหนดคีย์ครั้งเดียว (แชร์เว็บและมือถือ) และแต่ละภาษาเติมค่า "Due in {days} days" เป็นข้อความ ICU และการฟอร์แมตเงินมาจากฟอร์แมตเตอร์ที่รับ locale ไม่ใช่จากเครื่องหมายจุลภาคฮาร์ดโค้ด

ปล่อยการรองรับภาษาเป็นรายฟีเจอร์ ไม่ใช่การรีไรท์ครั้งใหญ่:

  1. เริ่มจาก flow ที่ผู้ใช้ใช้มาก (บิลลิ่ง การแนะนำ หรือเช็คเอาต์)
  2. ย้ายข้อความฮาร์ดโค้ดใน UI ไปที่ไฟล์แปลและแทนที่ด้วยคีย์
  3. เพิ่มข้อความพหุและการฟอร์แมตสำหรับ flow เดียวกัน
  4. ขยายไปฟีเจอร์ถัดไปเมื่อคีย์ที่ขาดใกล้เป็นศูนย์

เอกสารสองอย่างที่ควรเขียนเพื่อให้ฟีเจอร์ใหม่คงเส้นคงวา: กฎการตั้งชื่อคีย์ของคุณ (พร้อมตัวอย่าง) และ "definition of done" สำหรับสตริง (ไม่มีข้อความฮาร์ดโค้ด, ICU สำหรับพหุ, ฟอร์แมตสำหรับวันที่/ตัวเลข, เพิ่มในแค็ตาล็อกที่แชร์)

ขั้นตอนต่อไป: หากคุณสร้างใน Koder.ai ใช้ Planning Mode เพื่อกำหนดหน้าจอและคีย์ก่อนสร้าง UI จากนั้นใช้ snapshot และ rollback เพื่อทำซ้ำคำและการแปลบนเว็บและมือถืออย่างปลอดภัยโดยไม่เสี่ยงปล่อยผิดพลาด

คำถามที่พบบ่อย

ควรตั้งชื่อคีย์คำแปลอย่างไร?

ใช้คีย์ที่คงที่และอิงตามความหมาย เช่น billing.invoice.payNow แทนการใช้ประโยคภาษาอังกฤษทั้งประโยค จากนั้นคุณจึงปรับถ้อยคำได้โดยไม่ต้องเปลี่ยนการอ้างอิงในโค้ดหรือสร้างคำแปลซ้ำซ้อน

ข้อความใดบ้างที่ต้องใส่ไว้ในไฟล์คำแปล?

เก็บทุกข้อความที่ผู้ใช้มองเห็นไว้ในระบบคำแปลของคุณ รวมถึงข้อความผิดพลาด สถานะว่างเปล่า อีเมล การแจ้งเตือน และคำแนะนำระหว่างเริ่มใช้งาน บันทึกภายใน รหัสการวิเคราะห์ และฟิลด์ฐานข้อมูลมักไม่จำเป็นต้องแปล

จะทำให้ถ้อยคำบนเว็บและมือถือสอดคล้องกันได้อย่างไร?

ใช้แค็ตตาล็อกคีย์และข้อความตามโลแคลชุดเดียวกันสำหรับทั้งสองไคลเอนต์ React และ Flutter อาจใช้ไลบรารีคนละตัวได้ แต่ควรอ้างอิงชื่อคีย์และกฎของข้อความชุดเดียวกัน

แบ็กเอนด์ควรส่งข้อความผิดพลาดที่แปลแล้วหรือไม่?

ให้แบ็กเอนด์ส่งรหัสข้อผิดพลาดที่คงที่พร้อมค่าที่ปลอดภัย แล้วให้ไคลเอนต์แสดงข้อความที่แปลตามภาษา ตัวอย่างเช่น ส่ง auth.invalid_password แทนประโยคภาษาอังกฤษ

ควรจัดการรูปพหูพจน์ในภาษาต่าง ๆ อย่างไร?

ใช้ข้อความพหูพจน์แบบ ICU เพื่อให้แต่ละภาษาใช้กฎไวยากรณ์ของตนเองได้ ใส่ทั้งประโยคไว้ในข้อความเดียว แทนการเพิ่มตัวเลขให้กับคำนามที่แปลแยกต่างหาก

ควรเก็บไฟล์คำแปลไว้ที่ไหน?

เก็บโลแคลต้นฉบับ ซึ่งมักเป็นภาษาอังกฤษ ไว้เป็นแหล่งอ้างอิงหลัก และตรวจทานการเปลี่ยนแปลงพร้อมกับโค้ด จัดกลุ่มข้อความตามฟีเจอร์ และสงวนเนมสเปซที่ใช้ร่วมกันสำหรับการกระทำ ฟิลด์ การนำทาง และข้อผิดพลาดที่พบบ่อย

ต้องสร้างคีย์ใหม่ทุกครั้งที่ข้อความเปลี่ยนหรือไม่?

ใช้คีย์เดิมต่อไปหากความหมายยังเหมือนเดิม แม้ถ้อยคำจะเปลี่ยนไป สร้างคีย์ใหม่เมื่อความหมายหรือบริบทเปลี่ยนเท่านั้น แล้วลบคีย์เก่าหลังยืนยันว่าไม่มีสิ่งใดใช้งานอยู่

ควรแปลวันที่ เงิน และเขตเวลาให้เหมาะกับท้องถิ่นอย่างไร?

เก็บการประทับเวลาเป็น UTC แล้วจัดรูปแบบวันที่ เวลา ตัวเลข และสกุลเงินตามโลแคลและเขตเวลาของผู้ดู ใช้ตัวจัดรูปแบบที่รองรับโลแคล แทนการกำหนดเครื่องหมายจุลภาค สัญลักษณ์สกุลเงิน หรือรูปแบบวันที่ตายตัว

ควรทดสอบอะไรบ้างสำหรับภาษาที่เขียนจากขวาไปซ้าย?

ออกแบบทั้งหน้าจอในโลแคล RTL ไม่ใช่เฉพาะป้ายข้อความ ปรับทิศทางเลย์เอาต์ตามความจำเป็น กลับด้านไอคอนที่มีทิศทาง เช่น ลูกศรย้อนกลับ และทดสอบข้อความอาหรับกับละตินที่ปะปนกัน เช่น รหัสผลิตภัณฑ์

วิธีที่ง่ายที่สุดในการเพิ่ม i18n ให้แอปที่มีอยู่คืออะไร?

เริ่มจากขั้นตอนที่มีผู้ใช้จำนวนมาก เช่น การเริ่มใช้งาน การเรียกเก็บเงิน หรือการชำระเงิน ย้ายข้อความที่เขียนตายตัวของขั้นตอนนั้นไปไว้ในข้อความที่ใช้ร่วมกัน เพิ่มการรองรับพหูพจน์และการจัดรูปแบบ ทดสอบคำแปลที่ยาวขึ้น แล้วทำซ้ำกับฟีเจอร์ถัดไป

Related posts