-->

יום שלישי, 4 במרץ 2014

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

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

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

אנגליה: השפה היא כמו שפת המערכת הקיימת, אנגלית. גרמניה: יש לכם מתרגם שיתרגם בקלות לגרמנית ומישהו בחברה שיודע ספרדית היטב. תוך כמה ימים אתם יוצרים אקסל עם כל הודעות המערכת למשמש ("user strings") בכל 4 השפות.
סביבת הפיתוח מאפשרת ניהול מספר שפות ל User Strings (תהיה זו ג'אווה עם Resource Bundle property files או iOS עם NSLocalizedString).
האם זה יותר מעניין של כמה ימים - בכדי לכבוש את העולם המערבי המפותח? (פרגון לישראל)


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

אחד מחברי הפודקאסט סיפר שאפליקציית מובייל שהם כתבו הצליחה באופן מפתיע בשוק הרוסי והשוק הסיני - והם החליטו לתרגם את האפליקציה לשפות אלו. אני מכיר את נושאי רב-הלשוניות (Multi-lingual) מעולם ה Enterprise, אך נראה שהוא רלוונטי למדי גם לאפליקציות מובייל.

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

אני אנסה לשנות זאת... :)






מושגים


בתחום זה יש 3 מושגים מבלבלים:
  • Internationalization (בקיצור: i18n)
  • Localization (בקיצור: l10n)
  • Globalization (בקיצור: g11n)
ראשית הסבר על מהות הקיצורים המשונים:
המינוח Internationalization הוא דיי ארוך. מכיוון שאנשים מזהים מילים בעיקר בעזרת האות הפותחת והמסיימת, אפשר לקצר ל "i---n" ולשמור על היכולת של לקשר את הקיצור למילה המקורית. כמה אותיות יש באמצע? 18. לכן: i18n. (לא מאמינים? תספרו).

מה הקשר בין שלושת המושגים? האם Globalization הוא ההיפך מ Localization? האם זה אותו הדבר?

אפשר לסכם את הקשר בעזרת הנוסחה הבאה: g11n = i18n + l10n.

Internationalization הוא התהליך של הכנת התוכנה לריבוי שפות ואזורים (regions).

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

Globalization הוא התהליך הכולל של Internationalization ו Localization, הכולל הכנה לריבוי-שפות ואז התאמה למספר שפות/אזורים ספציפיים.


שיטה אחרת (עמוד 2 במסמך) מתארת את הקשרים מעט אחרת ומדברת גם על Localizability, L12y). אם האנשים שעובדים אתכם נוהגים ע"פ המודל השני, פשוט הפכו את שמות המושגים i18n ו g11n - כל השאר נשאר בערך אותו הדבר.


מונח נפוץ אחר, בעיקר ב API של שפות תכנות, הוא Locale (תרגום לעברית: מקום).

Locale הוא אובייקט המתאר קומבינציה של שפה ("עברית"), אזור ("קנדה") ולעתים גם codeset (שזה בעצם encoding, כמו UTF-8 - נושא שאפשר להקדיש לו פוסט משלו). בד"כ יהיה לנו על ה session אובייקט locale שבעזרתו נדע כיצד לשרת את המשתמש הנוכחי.

נהוג לקדד את ה Locale בעזרת שפה ע"פ תקן ISO 639-1 ואזור ע"פ תקן ISO 3166-1.
למשל: en_US (אנגלית, ארה"ב), en_GB (אנגלית, ארה"ב) או zh_CN (סינית בסין) או אפשר גם he_CN (עברית בסין).

Locale, אפרופו, מבטאים כ"לו-קאל" (כמו "לוראל" - חברת קוסמטיקה) ולא "לוקייל", כפי שלעתים נהוג.



חזרה ל FlappyWare


הכנסתם למערכת את כל משפטי הטקסט ב-3 השפות החדשות (גרמנית, צרפתית ועברית). אבל הלקוחות מתלוננים:
  • הישראלים רוצים שכל התפריטים ועימודי הדף יהיו מימין לשמאל. העימוד הנוכחי פשוט לא מתאים!
    אולי למתכנת, עימוד משמאל לימין לא מפריע (או עדיף), אך פועל במפעל, או אמא שיש לה מספיק נושאי-עניין בחיים - פשוט רוצים שהממשק יהיה קל ונוח ("למה לא עשו את זה בסדר? הם לא רואים שזה עקום?").
  • הבריטים רוצים שהמערכת תשאל: "?what is your favourite colour" ולא "?what is your favorite color". הבדל בין אנגלית-אמריקאית לאנגלית-בריטית, שנתפס אצל הבריטים כמאוד-לא-קטנוני.
  • ישנם באגים לא צפויים במערכת בספרדית בתחום החיפוש. משום מה גם הטבלאות התחרבשו... מה זה קשור לתרגום?!
  • הגרמנים פשוט לא מרוצים ונמנעים משימוש באפליקציה. לא הצלחתם לייצר איתם קשר טוב מספיק להבין מה בכלל הבעיה (?!).

כאשר המשתמשים שלכם מגיעים מתרבות שונה ודוברים שפה שונה, גם הטיפול בכל case/bug נהייה קשה יותר. כשלקוח טוען ש"בהונגרית זה צריך להיות ככה...", ואתם לא מבינים על מה הוא מדבר / למה זה יצא אחרת - זה יכול להיות מאמץ לא קטן להבין ולתקן.







להבין את תהליך התרגום


לפני שנבחן "מה יכול להשתבש?" (זה החלק המשעשע), כדאי לדבר גם על תהליך התרגום, שגם הוא עשוי מעט להפתיע. בגדול, ניתן לחלק את מה שנרצה לתרגם ל 2 סוגים ב 2 קטגוריות.
הקטגוריות הן:
  1. תוכן מערכת - כל הודעות טקסט במסופקות כחלק מהאפליקציה: כותרות, תפריטים, הודעות שגיאה וכו'. סוג זה הוא הנפוץ ביותר.
  2. תוכן לקוח - תוכן שהלקוח יצר בעצמו והוא רוצה לתרגם. סוג זה של תוכן רלוונטי כנראה רק לתוכנה עסקית, ורק לחלק קטן ממנה. לדוגמה: מערכת שיווק אונליין הנמכרת ללקוח (קמעונאי כלשהו) רוצה למכור סחורה במדינות שונות - לכן עליו לתרגם את קטלוג המוצרים, כתבי אחריות, מפרטים וכו' - תוכן שהוא יצר.
שני סוגי התוכן הם:
  1. מחרוזות (Strings), קרי "טקסט נטו" - אותן קל למדי לתרגם.
  2. כל סוג אחר של מדיה: תמונות, מסמכים (למשל PDF), קישורים וכו'.
    תמונות (כמו ה screenshot ב appstore או כפתורים באפליקציה) לעתים מכילות טקסט - ועל כן יש גם ליצור להן עותק מתורגם. ייתכנו תמונות שונות ללוקאלים שונים, למשל תמונה שונה לשוק החרדי ולשוק החילוני בארץ (כן... ניתן לראות את השוק החרדי כסוג של Locale שונה, למרות שהוא במדינת ישראל).
    לעתים אלו קישורים (URLs) לאתרים שונים, כאשר נקשר אתרים שונים לשפות / לוקאלים שונים.
את התרגום נוהגים לעשות מתוך ה Master Language. כלומר, בוחרים שפה "מובילה" (בארץ זו תהיה בד"כ אנגלית, אך במדינות אירופיות זו עשויה בהחלט להיות השפה המקומית) בה ייכתבו הטקסטים המקוריים וממנה יתורגמו כל השפות - בכדי לצמצם טעויות.

אפילו אם המערכת שאותה נרצה לתרגם היא מערכת online (למשל ממשק וובי בענן), את התרגום נהוג לא לבצע בתוך המערכת אלא במערכות תרגום ייחודיות. למשל: במקרה של תרגום תוכן-לקוח, עשוי להראות סביר לספק את היכולת בתוך המערכת, הרי יש כבר ממשק ווב. מה יותר נוח מזה?!

לרוב זה לא יהיה נוח מ 2 סיבות:
א. למתרגם לא תמיד תהיה גישה למערכת ה Online.
ב. כלי התרגום מספקים יתרונות משמעותיים (למשל מילון-מונחים שכבר בשימוש במערכת, Thesaurus ועוד) - והמתרגמים עשויים דווקא לגבות מחיר גבוה יותר על שימוש במערכת אונליין.






בעיניים עצומות

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

רוצים להימנע מתרגום נוסח Google Translate או תרגומים מביכים כגון "רפידת הטקסט" (TextPad)? - אם כן, כדאי שתעזרו למתרגמים.

מקור: גיקטיים

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

מה שעושים בדרך כלל (כשרוצים תוצאות יותר טובות) הוא מפרקים את הטקסטים מתוך קבצי המדיה השונים ושולחים אותם כ Strings. משאירים את הטיפול בפורמטים השונים של קבצי המדיה in-house.
אם המערכת שלכם מספקת יכולת לתרגם תוכן-לקוח - זה אומר לאפשר להוסיף מחרוזות שלא נמצאות במערכת.

דבר שני וחשוב יותר הוא הוספת תיאור קצר שמספק את ההקשר לכל מחרוזת. לדוגמה על "Hall of Fame" ניתן לציין שזוהי כותרת מסך עם תוצאות המשחק הטובות ביותר או דף באתר האינטרנט המאתר את מורשת החברה. התרגום - יהיה שונה ע"פ ההקשר שיסופק.

את ההערות מוסיפים פעמים רבות המתכנתים תוך כדי שהם כותבים את הקוד. כל מחרוזת שהיא טקסט לתרגום (לא כל המחרוזות בתוכנה הן כאלו, כמובן) - מתווספת עם הערה. יש סביבות בהן מוסיפים את ההערות בקוד (iOS?) וכאלו שבעזרת כלים נלווים ב IDE (למשל בג'אווה).

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

את המחרוזות לתרגום, נוהגים לנהל באחד מ-2 פורמטים נפוצים:
PO/POT - שמקובל בעולם ה Unix. מערכת Wordpress, למשל משתמשת בו.
XLIFF - מקובל יותר בעולם הג'אווה / אנטרפרייז. אפשר להמיר בקלות קבצי properties שמייצר Eclipse ל XLIFF.

עוד בסיס מידע חשוב הוא ה Translation Memory, מין "בסיס נתונים" (קובץ TMX או TBX) של המונחים בהם השתמשו במערכת. כאשר מתרגם יקבל מכם עוד עבודה אחרי חצי שנה שלא נגע במערכת (כאילו שהוא זוכר אותה) - ה Translation Memory יזכירו לו באלו מונחים הוא השתמש בעבר והוא יוכל לשמור על קונסיסטנטיות.

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


כיצד נראה התרגום בקוד


זה כמובן תלוי בשפת וסביבת הפיתוח. אני אצמד לדוגמאות ב Java - השפה בה אני מכיר את הנושא בצורה הטובה ביותר.
בג'אווה יש מנגנון בשם Resource Bundles שעוזר לנהל user strings בשפות שונות.
את ה Resource Bundle ניתן לייצר בעזרת Wizard ב Eclipse:



באותו הרגע נוצרים הקבצים הבאים:
  • קובץ plugin.properties שכולל את טקסטי ברירת-המחדל / ה Master Language.
  • לכל שפה, אזור או צימוד של השניים שהגדרנו - ייווצר קובץ נוסף, לדוגמה plugin_en.properties או plugin_en_GB.properties.


לאחר מכן ניתן לערוך את קבצי ה properties, כאשר כל key מייצג מחרוזת בכל שפה.


הערה: אצלנו בחברה היה Editor אחר, אולי פרי פיתוח פנימי. אנא סלחו אם אני מפספס פה איזה פרט.

בזמן ריצה של הקוד ישלוף את המחרוזת המתורגמת המתאימה:


ResourceBundle bundle = ResourceBundle.getBundle("properties.namespace", someLocale);

String translatedString = bundle.getString("someKey");


שימו לב שלג'אווה יש מנגנון fallback אם הוא לא מוצא את הלוקאל המדויק:
אם למשל הלוקאל הוא "en_US" אך אין קובץ כזה, הוא ינסה להשתמש ב "en". אם אין גם קובץ כזה - הוא ילך לקובץ ברירת המחדל (למשל: plugin.properties).



מה יכול להשתבש ב Localization?  - מיון


אם האפליקציה שלכם מקבלת קלט בשפה שאיננה אנגלית ומנסה למיין אותו - היא עלולה להיכשל.
נתחיל בכך שמתודת ()compareTo של המחלקה String בג'אווה עלולה כבר לגרום לטעות, אפילו בשפה האנגלית.
מדוע? בגלל הקידוד (להלן כמה יסודות שחשובים בכדי להבין).

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

הקידוד הבסיסי ביותר הוא קידוד בשם ASCII:
ASCII הוא קידוד 7-ביט. כלומר: כל אות יכולה להיות אחת מתוך 128 סימנים או אותיות.
32 התווים הראשונים הם control characters: טאב, רווח, מחיקה ועוד סימנים שרובם חסרי משמעות גרפית (למשל: סוף שורה). שאר התווים הם מספרים, אותיות וכמה סימני-שפה.
ישנן הרחבות לASCII של 8-ביט המוסיפות עוד כ 128 תווים המותאמים לשפות נוספות (כל הרחבה - לשפה אחרת). כלומר כל סימן/אות (character) הוא בית אחד / 8-ביט.


משפחת הסימנים הנראים של ASCII 8bit, בהתאמה לעברית (סימנים במסגרת הירוקה), מוצגים בתוכנת Character Map של "חלונות".

מיון בסיסי בשפת ג'אווה (וברוב השפות האחרות) מתבסס על הערך המספרי של האותיות במחרוזת.
כפי שניתן לראות, הערך המספרי של האות "Z" (גדולה) הוא 90, בעוד שהערך של "a" (קטנה) הוא גבוה יותר (97) - ולכן מיון יסדר מחרוזות בסדר הבא:
  1. Ant
  2. Zoo
  3. ambulance
...למרות שהיינו מצפים שהמילה ambulance תהיה במקום הראשון (סדר לקסיקוגרפי - כמו מילון).

פתרון נפוץ לאנגלית: לעשות מיון ע"פ צורת ה upperCase של המחרוזות.

אך זה לא עובד בשפות רבות אחרות....
  • בשוודית, ישנה אות הנכתבת כ ae (או סימן æ) - מקומה בסדר הלקסיקוגרפי הוא אחרי האות "z".
  • בגרמנית, כאשר האות s מופיעה ברצף פעמיים - מקצרים את הצמד לכתיבת ß (כמו ביטא). סידור ע"פ ערכי ASCII ימיין את המילה groß (שקול ל gross = "גדול") לאחר המילה grost - מכיוון שערך ה ASCII של ß גדול יותר. זו כמובן טעות צורמת לדובר גרמנית.
  • ע"פ חוקי הדקדוק של שפות אירופאיות שונות, אותיות מוטעמות ("accent") צריכות לבוא אחרי אותיות דומות לא מוטעמות. צרפתית גרועה אפילו יותר: אות מוטעמת מאוחרת היא חזקה יותר, כלומר: יש לבדוק את האותיות המוטעמות מימין לשמאל.


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


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

ג'אווה, כבר מגרסאות מוקדמות, כללה סט כלים עשיר ל i18n, רובם תחת החבילה java.text.
במשך השנים, התחזוקה והעדכון של ספריות אלו מעט הוזנח וכיום הן נחשבות עדיין טובות - אך לא "state of the art".
אם נושא ה i18n חשוב לכם באמת - הייתי ממליץ להשתמש בספריית ICU של IBM - ספריית open source בג'אווה (יש גם גרסת ++C) ל g11n המתוחזקת ברמה טובה.
יש גם רשימה של ספריות ג'אווהסקריפט לתמיכה ב i18n - אולם אין לי ניסיון עמן ואני לא יודע להמליץ.

בג`אווה, הביטוי הבא יבצע השוואה נכונה, ע"פ השפה הנתונה:


Collator.getInstance(Locale.FRENCH).compare(stringA, stringB)


ל ICU יש גרסה משלה ל Collator - מדויקת יותר.
אם אתם מתכוונים לעשות הרבה השוואות על אותה קבוצת מחרוזות (למשל: מיון תכוף), collator איננו מאוד יעיל ועדיך להשתמש ב CollatorKeys.



סיכום

טוב, כיסינו רק את נושא המיון - אך יש עוד מספר מקרים שכדאי להתייחס אליהם (חיפוש, עיבוד מחרוזות, html וכו') אותם אני מקווה לכסות בפוסט המשך.

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

כמובן שיש לתהליך globalization מלא יש עלות, במיוחד אם מתחילים לעשות שינויים בקוד (עבור חיפוש, מיון וכו'). כדאי להכיר את הנושאים ולהשתמש בפוסט(ים) כ reference לאלו בעיות יכולות לצוץ, ולבחור ע"פ מצב השוק - כמה רחוק ללכת.


שיהיה בהצלחה!



----

לינקים רלוונטיים:

סקירה מקיפה על פורמטים וכלים על תרגום (אם המערכת שלכם תנהל תרגום): http://edutechwiki.unige.ch/en/Software_localization
התרגומיה, בלוג עברי על תרגום :http://yaeltranslation.com



תגובה 1:

  1. אני חרדי, מסכים איתך לגבי Locale . . .

    השבמחק