ข้ามไปที่เนื้อหา

การย้ายระบบ

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

คำถาม คำตอบ
ไฟล์ .po และ .mo เดิมยังใช้ได้ไหม ได้ ไฟล์เดิม เครื่องมือเดิม
การเรียกแบบเก่ากับแบบใหม่อยู่ในไฟล์เดียวกันได้ไหม ได้ และ mapping ของตัวสกัดอันเดียวครอบคลุมทั้งสองแบบ
msgid เปลี่ยนไหม ไม่เปลี่ยนถ้ามาจาก .format() เปลี่ยนถ้ามาจาก %-format
ทั้งโปรเจกต์ต้องย้ายพร้อมกันไหม ไม่ต้อง จุดเรียกใช้เดียวก็เป็นการเปลี่ยนที่ใช้ได้แล้ว
แล้ว Jinja เทมเพลตของ Django หรือ JavaScript ล่ะ ไม่ถูกแตะต้อง ใช้แคตตาล็อกเดิม

ส่วนที่เหลือของหน้านี้คือรายละเอียดเบื้องหลังแต่ละข้อ

จาก .format(): msgid ไม่เปลี่ยน

นี่คือกรณีที่การย้ายแทบไม่มีต้นทุนเลย ข้อความแบบ str.format กับข้อความแบบ t-string อนุมานคีย์แคตตาล็อก เดียวกัน เพราะคีย์คือข้อความที่ยังมี {name} อยู่ข้างในทั้งสองแบบ:

# Before
_("Hello {name}").format(name=name)

# After — the msgid is still "Hello {name}"
tr(t"Hello {name}")

คำแปลที่มีอยู่จึงยังติดอยู่กับข้อความนั้นต่อไป เริ่มจากแคตตาล็อกที่มี

#: app.py:6
#, python-brace-format
msgid "Hello {name}"
msgstr "こんにちは {name}"

ให้แก้จุดเรียกใช้ สกัดใหม่ แล้วอัปเดต:

$ pybabel extract -F babel.cfg -o locales/messages.pot .
extracting messages from app.py (encoding="utf-8")
writing PO template file to locales/messages.pot
$ pybabel update -i locales/messages.pot -d locales
updating catalog locales/ja/LC_MESSAGES/messages.po based on locales/messages.pot

รายการที่กลับมาต่างออกไปเพียงเมทาดาทาสองบรรทัดเท่านั้น ไม่มีอย่างอื่นอีก — คอมเมนต์เครื่องหมายที่ระบุว่ามันเป็นข้อความแบบ t-string และหมายเลขบรรทัดในซอร์ส:

#. gettext-tstrings
#: app.py:4
#, python-brace-format
msgid "Hello {name}"
msgstr "こんにちは {name}"

ไม่มีธง fuzzy ไม่ต้องแปลใหม่ ไม่ว่าภาษาใดก็ตาม ข้อความเรนเดอร์ได้ทันที:

$ pybabel compile -d locales
compiling catalog locales/ja/LC_MESSAGES/messages.po to locales/ja/LC_MESSAGES/messages.mo
$ python app.py
こんにちは Ada

update --check จะรายงานว่าแคตตาล็อกล้าสมัย

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

จาก %-format: msgid เปลี่ยน คำแปลจึงกลายเป็น fuzzy

ไวยากรณ์ printf อยู่ ข้างใน ตัวข้อความ การแทนที่มันจึงเขียนคีย์ของแคตตาล็อกใหม่ เรื่องนี้เลี่ยงไม่ได้ และมันคือต้นทุนตามจริงของการทิ้ง %(name)s ไว้เบื้องหลัง:

# Before
_("Hello %(name)s") % {"name": name}

# After — a different msgid
tr(t"Hello {name}")

pybabel update รู้ว่าข้อความใหม่เป็นญาติใกล้ชิดของข้อความที่ถูกลบไป จึงพาคำแปลเดิมข้ามมาให้ พร้อมติดธง fuzzy:

#. gettext-tstrings
#: app.py:4
#, fuzzy, python-brace-format, python-format
msgid "Hello {name}"
msgstr "こんにちは %(name)s"

มีสามเรื่องที่ควรรู้เกี่ยวกับสภาวะนั้น:

  • ไม่มีอะไรพังตอนรันไทม์ รายการ fuzzy ถูกกันออกจาก .mo ที่คอมไพล์แล้ว แอปพลิเคชันจึงเรนเดอร์ข้อความต้นทางจนกว่ามนุษย์จะยืนยันคู่นั้น — การถอยกลับแบบเดียวกัน กับที่ข้อความซึ่งถูกแก้ถ้อยคำใด ๆ ต้องผ่าน
  • CI ยังเขียวอยู่ตลอดช่วงที่มันเป็น fuzzy ตัวตรวจตัวยึดตำแหน่งข้ามรายการ fuzzy ไป เช่นเดียวกับที่ msgfmt --check-format ทำ เพราะรายการที่ไปไม่ถึงรันไทม์ ไม่ควรทำให้ build ล้ม และทันทีที่นักแปลปลดธงนั้นออก รายการดังกล่าวจะถูกตรวจเหมือนรายการอื่น ๆ — ดังนั้น %(name)s ที่ค้างอยู่ในคำแปลที่ได้รับการยืนยันแล้วจะถูกจับได้ในตอนนั้น ซึ่งเป็นจุดเดียวกับที่มันจะเริ่มถูกเรนเดอร์
  • ธง python-format เก่าติดมาด้วย และควรถูกลบพร้อมกับธง fuzzy มิฉะนั้น msgfmt --check-format จะเอากฎของ printf ไปใช้กับข้อความแบบ brace-format ต่อไป

สำหรับตัวยึดตำแหน่ง printf แบบมีชื่อ การแก้ไขเป็นงานเชิงกล — %(name)s กลายเป็น {name} และไม่มีอย่างอื่นขยับ — แคตตาล็อกขนาดใหญ่จึงเป็นการรันสคริปต์ผ่านหนึ่งรอบ ตามด้วยการรีวิวของนักแปล ไม่ใช่การแปลใหม่ทั้งหมด ส่วน %s แบบระบุตำแหน่งไม่ใช่งานเชิงกล: มันไม่มีชื่อให้ยกข้ามมา และการเลือกชื่อให้มันก็คือแก่นของการเปลี่ยนแปลงนี้

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

การเรียกแบบเก่ากับแบบใหม่อยู่ร่วมกันได้

ตัวสกัดที่อ่าน t-string ได้ก็อ่านการเรียก gettext ธรรมดาได้ด้วย mapping อันเดียวจึงครอบคลุมไฟล์ที่อยู่ระหว่างการย้าย:

[gettext_tstrings: **.py]
encoding = utf-8
from gettext_tstrings import tr
from myapp.i18n import _

name = "Ada"
print(_("Save changes"))
print(tr(t"Hello {name}"))

ข้อความทั้งสองลงไปอยู่ในเทมเพลตเดียวกัน และมีเพียงข้อความแบบ t-string เท่านั้น ที่พกคอมเมนต์เครื่องหมายซึ่งเปิดการตรวจสอบเพิ่มเติมของไลบรารีนี้:

#: app.py:5
msgid "Save changes"
msgstr ""

#. gettext-tstrings
#: app.py:6
#, python-brace-format
msgid "Hello {name}"
msgstr ""

มันรู้จัก _() ชื่อ gettext มาตรฐานทั้งสี่ alias tr() / ntr() และ lazy_gettext() / lazy_pgettext() แบบเลื่อนเวลา ส่วนฟังก์ชันช่วยของคุณเองต้องถูกระบุชื่อไว้ใน mapping

ตอนรันไทม์ทั้งสองรูปแบบเป็นอิสระต่อกันเท่า ๆ กัน: gettext.translation() คืนอ็อบเจกต์การแปลหนึ่งอ็อบเจกต์ และทั้ง _ กับจุดเข้าของไลบรารีนี้ต่างก็อ่านจากมัน

สิ่งที่ไม่ต้องย้าย

  • ภาษาเทมเพลต {% trans %} ของ Jinja2, template tag ของ Django และตัวสกัดของ Babel สำหรับสิ่งเหล่านั้นยังทำงานได้เหมือนเดิม และยังคงป้อนเข้าสู่แคตตาล็อก PO เดียวกัน t-string เป็นไวยากรณ์ของ Python มันจึงใช้กับซอร์ส Python
  • ไฟล์แคตตาล็อกของคุณ ไม่มีการเปลี่ยนรูปแบบไฟล์ ไม่มีไฟล์ใหม่ ไม่มีขั้นตอนแปลง
  • แพลตฟอร์มการแปลของคุณ การแลกเปลี่ยนด้วย .po เหมือนเดิมทุกประการ และธง python-brace-format ที่ข้อความแบบ t-string พกไว้ก็เป็นธงเดียวกับที่ข้อความแบบ .format() พกไว้ — QA ตัวยึดตำแหน่งจึงยังทำงานต่อไปได้
  • โค้ดที่ไม่ใช่ Python แคตตาล็อกของ JavaScript หรือ C ในโปรเจกต์เดียวกันไม่ได้รับผลกระทบ

เช็กลิสต์การย้าย

  1. เพิ่ม extra babel ในที่ที่ pybabel ทำงาน แล้วเปลี่ยน mapping ของ python ใน babel.cfg ให้ใช้เมธอด gettext_tstrings — mapping อันเดียวจะครอบคลุมทั้งสองรูปแบบ และ -k ก็ยังใช้ได้กับการเรียกแบบธรรมดา
  2. แปลงจุดเรียกใช้แบบ .format() ก่อน สกัดใหม่ รัน pybabel update แล้วคอมมิตแคตตาล็อกไปพร้อมกับโค้ด คาดหวังได้ว่าจะไม่มีรายการ fuzzy เลย
  3. แปลงจุดเรียกใช้แบบ %-format เป็นชุด ๆ เท่าที่คุณจะได้รับการรีวิวไหว โดยเขียนตัวยึดตำแหน่งที่ถูกยกข้ามมาใหม่ และปลดธง fuzzy กับ python-format ออก
  4. แก้สิ่งที่ข้อจำกัดปฏิเสธ: การแทรกค่าต้องเป็นชื่อเปล่า ๆ ดังนั้น t"Hello {user.name}" ต้องกลายเป็นตัวแปร local ก่อน นี่คือการแก้ที่จุดเรียกใช้ ไม่ใช่ที่แคตตาล็อก
  5. เปิด strict = true ใน mapping ของตัวสกัดเมื่อกวาดเสร็จแล้ว เพื่อให้ข้อความที่สกัดไม่ได้ทำให้ build ล้มเหลว แทนที่จะหายไปจากเทมเพลตเฉย ๆ
  6. เพิ่มการตรวจตอนรันไทม์จากการใช้งานจริง: เรนเดอร์หนึ่งข้อความต่อหนึ่งภาษาที่จัดส่ง ผ่าน Translator แบบเข้มงวด

ขั้นตอนที่ 2 และ 3 เป็นคอมมิตธรรมดา ไม่มีข้อใดในรายการนี้ที่ต้องการวันดีเดย์