לדלג לתוכן

מלכודות נפוצות

האתר הזה מתורגם לשלושים וחמש שפות, וכל אחת מהמהדורות האלה הופקה בהרצת הלולאה שהתיעוד הזה מלמד. זהו מאגר קטן בקנה המידה של התעשייה, ובכל זאת די היה בו כדי להיתקל ברוב המלכודות שהופכות בינאום לקשה יותר משהוא נראה.

כל אחד מהסעיפים שלהלן הוא משהו שבאמת השתבש כאן, איך זה נראה באותו רגע, והיכן עובר הקו בין מה שהספרייה בודקת עבורכם לבין מה שנשאר לשיקול דעתכם.

שינוי שם של משתנה מתרגם משפט מחדש

ה-msgid הוא מפתח הקטלוג, ושם שעבר אינטרפולציה נמצא בתוכו. העברת קבוע אחד לתחום המודול וכתיבתו באותיות גדולות כפי שסגנון Python מבקש — מ-author ל-AUTHOR — הפכה את Copyright © 2026 {author} · MIT License להודעה ששום קטלוג לא ראה מעולם. כל תרגום של השורה הזו היה חוזר דרך מחזור ה-fuzzy, בכל שפה, בשביל שינוי שם שלא שינה דבר שקורא יכול לראות.

הספרייה לא תעצור אתכם: שני האיותים הם שמות תקפים של מציין מקום. מה שהיא כן עושה הוא להפוך את השם לראוי להגנה — אינטרפולציה חייבת להיות שם פשוט, ולכן מה שנמצא במפתח הקטלוג הוא מילה שמתרגם יכול לקרוא, לא ביטוי.

המקרה ההפוך בטוח מעצם הבנייה. המרות ומפרטי פורמט אינם חלק מה-msgid, ולכן הידוק {amount:,.2f} ל-{amount:,.0f} אינו משנה אף מפתח ואינו מבטל אף תרגום בשום מקום.

nplurals=2 אינו אומר שתי מחרוזות שונות

טורקית, הונגרית, פרסית ובנגלית מצהירות כולן על שתי צורות ריבוי, ובכל ארבעתן שתי הצורות של הודעה ספורה הן בדין אותה מחרוזת — שם העצם נשאר ביחיד אחרי מספר, ולכן {n} sayfa נכונה לעמוד אחד ולעשרה. מבקר ש"מתקן" את הכפילות שובר את התרגום.

הטעות ההפוכה קלה בדיוק באותה מידה. הצורה השלישית של לטבית קיימת לאפס בלבד; השנייה של סלובנית היא זוגי, לשניים בדיוק; הצורה האחרונה של רומנית מחייבת את המילה de שאסור שתופיע בשתי הראשונות. מילוי המשבצות האלה ביחיד וברבים מפיק קטלוג ששגוי רק במונים שאיש אינו בודק.

גרוע מכך, סדר המשבצות אינו סמנטי. וולשית ממספרת את חמש צורותיה כך ש-msgstr[0] הוא המקרה הכללי ו-msgstr[1] הוא היחיד. מילוין ברצף המתבקש שם את היחיד בדיוק במקום שבו כל הודעה בלתי ספורה תמצא אותו.

הספרייה אינה נוטלת על עצמה דבר מכל זה, וזו בדיוק הנקודה: כלל הריבוי של שפת היעד חי בכותרת הקטלוג שלה עצמה, וכלל האיחוד/החיתוך מאפשר לתרגום להחזיק יותר צורות מהמקור, או פחות. מה שהיא כן בודקת הוא הדבר היחיד שניתן לבדוק בלי לדעת את השפה — שכל צורה שומרת על מצייני המקום שהיא זקוקה להם.

שתי צורות יכולות להיות זהות מסיבה טובה

לאירית חמש צורות ריבוי, ובדוח הבנייה של האתר הזה כמה מהן נכתבות באותו אופן בדיוק. זו אינה פליטת העתק-הדבק: המילה leathanach מתחילה ב-l, ואף אחת משתי המוטציות הפותחות שמספרים באירית מחוללים אינה נכתבת על l. הצורות עדיין עושות עבודה אמיתית — הגזע מתחלף בין leathanach ל-leathanaigh, ומונים מעל עשר חוזרים ליחיד — אבל שום שם עצם שמשמעותו "עמוד" לא היה מראה את ההבדל.

כל בדיקה שמסמנת צורות כפולות כחשודות תסמן אירית תקינה. אדם שמכיר את השפה הוא המבקר היחיד לעניין הזה.

הודעה יכולה להתאים רק למונה אחד

דוח הבנייה של האתר הזה מדווח כמה עמודים רונדרו וכמה זמן זה לקח. כתיבתו בתור "Rendered {n} pages in {seconds} seconds" נראית בלתי מזיקה ואינה ניתנת לתרגום: gettext בוחר צורה אחת לפי מונה אחד, והמונה הזה הוא n. המילה seconds הייתה צריכה להתאים למספר שמנגנון הריבוי לעולם אינו רואה.

התיקון הוא להפוך את הכמות השנייה לסמל יחידה במקום למילה, וסמלי היחידות בעצמם עוברים לוקליזציה: הקטלוגים של האתר הזה נושאים s, с, ث, שנ׳ ו-mp, והטיפוגרפיה הצרפתית, הספרדית והשוודית דורשות רווח לפני הסמל במקום שבו האנגלית אינה דורשת. שום דבר מזה אינו עניינה של הספרייה — אבל לשים לב שהודעה זקוקה לשתי התאמות דווקא כן, והכלי היחיד לכך הוא לכתוב את ההודעה אחרת.

עריכת משפט באנגלית עורכת דקדוק זר

דף הבית נהג לומר "all ten language editions". הסרת המספר — עריכה של מילה אחת באנגלית, שנעשתה מפני שהמספר הוסיף להתיישן — הפכה נושא ברבים ליחיד. ספרדית, איטלקית, פורטוגזית, רוסית, אוקראינית, יוונית, הולנדית ועברית נאלצו כולן להתאים מחדש את הפועל; בכמה מהן היה צריך לשנות גם את הבינוני.

עריכת מקור שנקראת כזניחה באנגלית אינה זניחה במורד הזרם. סימונה כ-fuzzy, וזה מה ש-pybabel update עושה, הוא המנגנון שנותן לכל מתרגם את ההזדמנות לשים לב.

הבדלים בלתי נראים שורדים כל העתק-הדבק

המדריך מצטט אבחון שמכיל (nаme) — מילוט מכוון, מפני שהתו שהוא מציין הוא а קירילית שאף קורא אינו יכול להבחין בינה לבין הלטינית. מתרגמי האתר הזה הפכו את המילוט הזה לתו עצמו חמש פעמים נפרדות, בחמש שפות שונות, ובכל פעם הפיקו עמוד שנראה נכון והיה שגוי.

את זה הספרייה כן תופסת, וזו הסיבה לצורתם של האבחונים: מציין מקום שהאותיות שלו מערבבות מערכות כתב מדווח פעמיים, פעם באופן קריא ופעם ממולט, מפני שהצורה הממולטת היא האיות היחיד שמבדיל ביניהם. רווח קשיח בתוך סוגריים מסולסלים מודפס לפי נקודת הקוד שלו מאותה סיבה. בודק הקטלוגים דוחה את ההודעה לפני שהיא יכולה להישלח.

לא ריק אינו מתורגם

קטלוג שנבנה כשלד כשה-msgid שלו הועתקו אל ה-msgstr עובר כל בדיקה נאיבית: שום דבר אינו ריק, שום דבר אינו fuzzy, וקבוצת ההודעות תואמת במדויק. מהדורה אחת של האתר הזה נשלחה כך במשך כמה שעות. כך גם שמונה עמודים ממהדורה אחרת שהיו עותקים זהים בייט-בייט של המקור האנגלי — שעוברים בדיקה המשווה את בלוקי הקוד ביניהם, מפני שהם אותו קובץ.

אף אחד מהשניים אינו דבר שספריית תרגום יכולה לראות. את שניהם זול לבדוק, אבל לא על ידי דרישה שכל רשומה תהיה שונה מהמקור שלה: ‏OK, שמות מוצרים, שמות של אנשים, ראשי תיבות ומזהי קוד — כולם מתורגמים לעצמם, ובדיקה שאוסרת זאת מייצרת התרעות שווא לנצח.

מדדו במקום זאת את השיעור, על פני קטלוג שלם או עמוד שלם, ושלחו את החריגים לבן אדם. הבדיקה של האתר הזה עושה בדיוק את זה — היא משווה את שורות הפרוזה של כל מהדורה מול המקור האנגלי ונכשלת מעל 25% זהות. המהדורה המזויפת עמדה על 87%; כל תרגום אמיתי עומד בין 4% ל-8%, שהם הזנב הקטן של שורות שמתלכדות באופן לגיטימי, כמו כתובות URL ופלט תוכנית מצוטט. שתי האוכלוסיות רחוקות זו מזו מספיק כדי שהסף לא יצטרך להיות מדויק.

הקטלוג אינו הדבר המתורגם היחיד

לשני כשלים כאן לא היה שום קשר ל-gettext.

תרגום כותרת משנה את העוגן הנגזר ממנה, ולכן כל קישור מעמוד אחר אל הסעיף הזה נשבר — בשקט, ובאותה שפה בלבד. האתר הזה מצמיד את העוגן האנגלי לכל כותרת, ובדיקה גוזרת את הרשימה הצפויה מן העמוד האנגלי.

ומחולל האתר מספק תרגומי ממשק לשישים ושמונה שפות, ובהן אין סוואהילית ואין אירית. בלעדיהם הבנייה אינה מתדרדרת לאנגלית; הכללת התבנית נכשלת ואי-אפשר לבנות את המהדורה כלל. שני קבצים מקבצי המאגר הזה עצמו קיימים כדי לסתום את הפער הזה.

גם לכלים שלכם יש באגים

שלב ה-CI שהתיעוד הזה ממליץ עליו לתפיסת קטלוגים מיושנים, pybabel update --check, אינו יכול לעשות את העבודה הזו באף פרויקט שמשתמש ב-pgettext או ב-npgettext. ב-Babel 2.18.0 הוא מדווח על כל קטלוג שיש בו msgctxt כלא מעודכן, בכל הרצה. ההשוואה עוברת דרך Catalog.is_identical, שמחפשת כל הודעה לפי המפתח שתחתיו היא נשמרת — ועבור הודעה הקשרית המפתח הזה הוא הצמד (id, context), ש-Catalog.get אינו מקבל. החיפוש אינו מחזיר דבר, והקטלוגים לעולם אינם יוצאים שווים:

>>> from babel.messages.catalog import Catalog
>>> c = Catalog(locale="ja")
>>> c.add("Guide", "ガイド", context="navigation")
<Message 'Guide' (flags: [])>
>>> c.is_identical(c)
False

הוא נמצא כאן מתוך ניסיון להשתמש בו, דווח במעלה הזרם, והבדיקה החלופית נמצאת בעמוד סביבת הייצור.

הלקח הכללי הוא הלא נוח: שער שתמיד אדום גרוע משער שאינו קיים, כי צוות מכבה אותו. ודאו שבדיקת ה-CI שלכם באמת מסוגלת לעבור לפני שאתם סומכים עליה שתיכשל.

בשביל מה הספרייה הזו, בשורה אחת

רוב העמוד הזה הוא שיקול דעת ששום כלי אינו יכול ליטול על עצמו. מה שכלי כן יכול לעשות הוא להבטיח שתרגום לא יוכל לשנות את מבנה המשפט שהוא מתרגם — לא להשמיט ערך, לא להמציא ערך, לא לפרמט ערך מחדש ולא להושיט יד אל האובייקטים שלכם — ולומר זאת במשפט שהאדם שצריך לתקן יכול לפעול לפיו. זה כל מה שהספרייה הזו מבטיחה, ושאר האתר הזה הוא איך היא עומדת בזה.