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