תרגום תוכנה באמצעות gnu Gettext

GNU Gettext הוא כלי המאפשר ליצור קבצי תרגום עבור התוכנה שאנחנו כותבים ללא קשר לשפת התכנות או לשפת בני האדם המקורית בה כתבנו את התוכנה.

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

GNU Gettext מכיל שני סוגי פורמטים של קבצי תרגום, הראשון הוא קובץ טקסט (קובץ po) אשר מכיל את המילה/משפט המקורי, ומקום לכתוב את התרגום שלנו לאותה מילה/משפט. קובץ ה po הוא בעצם ה"קוד מקור" שלנו.

הקובץ השני הוא קובץ בינארי, אשר מכיל את התרגום בצורה מהודרת, ובכך מאיץ את יכולות הטעינה והשימוש. לקובץ הבינארי בד"כ מתקבלת הסיומת mo .

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

בפסקל יש לנו בנוסף ל const עוד חלק של "קבוע" בשם resourcesting, וכמו שהשם שלו מרמז, משתמשים בו בשביל לשמור טקסט קבוע שנשמר בתור משאב.

אז נתחיל בכתיבת יחידה עם resourcestring:

unit TestingGnuGetText;interface

resourcestring

sText1 = 'Text1';

sText2 = 'Text2';

implementation

end.

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

נשתמש בתוכנה rstconv שמגיעה עם FPC, ונמיר את הטקסט לפורמט של po (אפשר גם לפורמט של rc ופורמט של msg מבית IBM). אנחנו כמובן נמיר לקובץ po:

rstconv -i testinggnugettext.rst -o translate.po -f po

וזהו נקבל קובץ po מוכן לתרגום. לאחר שנתרגם את הקובץ, אנחנו נשתמש בתוכנה msgfmt:

msgfmt -o translate.mo translate.po

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

program ...uses gettext, TestingGnuGetText … ;…

begin

writeln(sText1);

TranslateUnitResourceStrings('TestingGnuGetText', 'translate.mo');

writeln(sText1);

end.

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

procedure TranslateResourceStrings(AFile: TMOFile);
procedure TranslateUnitResourceStrings(const AUnitName:string; AFile: TMOFile);
procedure TranslateResourceStrings(const AFilename: String);
procedure TranslateUnitResourceStrings(const AUnitName:string; const AFilename: String);

כאשר כל אחת מהאפשרויות נותנת לנו דרך לתרגם את הטקסט שאנחנו רוצים.

11 מחשבות על “תרגום תוכנה באמצעות gnu Gettext

  1. ארתיום

    מס' הערות:

    אני לא יודע איך בפסקל אבל ב־C כשמשתמשים ב־gnu gettext דב"כ מתייחסים ל־locale. כך למשל אם ה־LC_MESSAGES=he_IL.UTF8 אז ייבחר הקובץ מתוך ‎/usr/share/locale/he/LC_MESSAGES/youdomain.mo

    כפי שאני אני רואה לפי ה־API אז מה שניתן זה הקובץ ולא locale+domain. או שאני טועה.

    בנוסף, אתה יכול להסביר כיצד עובד ה־writeln השני? הוא אוטומטית מתרגם את הטקסט? או שאני מפספס משהו, כי אם כן יהיו לי עוד כמה שאלות אליך.

    מספר הערות:

    אני לא יודע לגבי המימוש של pascal אבל gnugettext המקורי אני thread safe כך שאתה לא יכול להחליף תרגום מתי שחפץ לך אלא אם כן אתה משנה את התרגום בכל התוכנה.

    שגיאה קטנה: לא "msgfmt -o translate.mo translate.mo" אלא
    msgfmt -o translate.mo translate.po

    (למה אני מתעניין? כי לא מזמן יצא לי לכתוב מימוש threadsafe של gettext).

  2. ik_5 מאת

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

    לפי הקוד של gettext.pp יש את האפשרות של locale+domain.

    ב writeln השני קיבל העתק על אותו "שם" (sText1), רק עם התוכן החדש. אם יש לך כמה thread אז אתה צריך להפוך אותם לsuspend לטעון מחדש את התרגום ולהפעיל את ה thread לפי מה שאני מבין מהסבר שקיבלתי, אבל לא ניסיתי לבד, אז יכול להיות שיהיה אפשרי רק להריץ מחדש את התוכנית.

    בקשר ל msgfmt, תוקן תודה

  3. ארתיום

    זהו אני רואה עם הגישה שהסברת קצת בעיות.

    למשל, מומלץ להשתמש בדברים כמו printf(_("Wellcome %s"),name);‎

    על מנת קבל את ההקשר, כיצד זה עובד שם?

    איך התמיכה ב־ngettext לצורות ריבוי השונות?

  4. ik_5 מאת

    אני לא בטוח שאני מבין אותך. יש אפשרות להשתמש ב format string
    אבל זה לא אומר שאני חייב להשתמש בו.

    writeln(format('Welcome %s', [name]));
    אבל אם אתה משתמש ב resourcestring
    אז אתה תשתמש בקבוע שיצרת שהוא מכיל את ה format string.

    אבל אני ממש לא בטוח שהבנתי על מה אתה מדבר

  5. GuySoft

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

    במיוחד מפני שיש סקריפט חיצוני שמצאתי שמייצר את קבצי ב-pot לבד:
    http://wiki.wxpython.org/Internationalization?action=AttachFile&do=view&target=mki18n.py

    כלומר שלא צריך לדעת כמעט כלום. רק משהו כמו 4 שורות של קוד. וכל תוכנה הופכת לתוכנה עולמית.

  6. ארתיום

    הבנתי…

    פשוט, מניסיון רבים מאוד, זה לא יעיל בעליל (מבחינת הפיתוח) לכתוב:

    stringxyz:="Hello";‎
    writeln(stringxyz);‎

    כי אתה פשוט לא מבין מה זה הסטרינג הזה. ב־Windows (או ליתר דיוק Win32API) בחרו בשיטה שלכל מחרוזת מתאים איזשהו resource ואתה מציין את ה־resource (מאקרו) במקום הטקסט עצמו — זה הפך את התמיכה ב־i18n לסיוט, לעומת זאת, עבודה עם gettext מאוד שקופה רק תכתוב במקום:

    printf("Hello\n");‎
    את
    printf(_("Hello\n"));‎

    והקוד הופך להרבה יותר קריא.

  7. ik_5 מאת

    זהו שאתה לא כותב
    stringxyz := "Hello;

    אתה כותב את זה בתור קבוע. ההבדל הוא, שהוא מאפשר לך לתת עותק עם ערך שונה, ויותר מזה, לא משנה לו השיטה שאתה משתמש בשביל לתרגם דברים. זה יכול להיות genu gettext, זה יכול להיות rc של Windows, ויזה יכול להיות קובץ ini שאתה יצרת בשביל תרגום. זה פשוט לא משנה. הלוגיקה שלך אף פעם לא משתנה. רק אמצעי התרגום שלך משתנה.
    מה שהופך את השיטה להיות עם הרבה כוח ויכולות. למשל אני לא מתעסק עם תרגום מספר ה string resource לבין המשתנה שמכיל את התוכן. זה לא מעניין אותי, בשביל זה יש לי כלי שעושה את זה בשבילי.

    השימוש בקו תחתי בתור שם פונקציה, הוא דבר שלדעתי מאוד לא ידידותי. וגם השיטה ל gnu gettext היא מאוד נקודתית. מה שאומר שאם מחר אתה תרצה לעבוד בשיטה אחרת, או שתעבוד מאוד קשה, או שאתה תיישם עוד פונקציה בשם קו תחתי ותקרא ל include שלה במקום ל include של gettext.

  8. artyom

    הערה קטנה: "_" זה בסה"כ define והוא לא מוגדר כברירת מחדל, נוהגים להגדיר אותו אבל זו שאלה של טעם. למשל, אפשר לכתוב gettext מלא או לעשות define אחר.

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

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

  9. ik_5 מאת

    דווקא hello word זה overkill רציני ל gettext. אבל הנה:

    program helloword;
    uses gettext,SysUtils;

    resourcestring
    sHelloWorld = 'Hello world';

    begin
    TranslateResourceStrings(GetEnvironmentVariable('LANG')+ '.mo');
    writeln(sHelloWorld);
    end.

  10. פינגבק: Precision Language Suite « לראות שונה

  11. פינגבק: אתר עם שפה « לראות שונה

להשאיר תגובה

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

הלוגו של WordPress.com

אתה מגיב באמצעות חשבון WordPress.com שלך. לצאת מהמערכת / לשנות )

תמונת Twitter

אתה מגיב באמצעות חשבון Twitter שלך. לצאת מהמערכת / לשנות )

תמונת Facebook

אתה מגיב באמצעות חשבון Facebook שלך. לצאת מהמערכת / לשנות )

תמונת גוגל פלוס

אתה מגיב באמצעות חשבון Google+ שלך. לצאת מהמערכת / לשנות )

מתחבר ל-%s