האם אתה מוכן לבינאום ולוקליזציה?

ב־19.4.2009, מאת ארתיום; פורסם תחת: לינוקס, פיתוח, תכנה ומחשבים, C++‎‏, Unicode; ‏6 תגובות

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

  1. אני רוצה לייצג מחרוזת, כ־char *some_text. מהו המידע שחסר לי?
  2. יש לי מחרוזת wchar_t *text או String text ב־Java, אני רוצה לקחת את התו הראשון שלה: wchar_t c=text[0]; // C Char c=text.charAt(0); // Java האם זהו קוד נכון?
  3. אני כותב את הקוד הבא: if n==1: print translate("You have one aplle") else: print translate("You have %d apples") % n מה לא בסדר בקוד הזה?
  4. מה לא בסדר בקוד הבא: #include <stdio.h> #include <time.h>

     int main()
     {
             time_t now_t=time(NULL);
             struct tm *now=localtime(&now_t);
             char str_time[32];
             strftime(str_time,sizeof(str_time),"%d/%m/%y",now);
             printf("Today is %s\n",str_time);
             return 0;
     }
    
  5. מהו גודל הזכרון הנדרש לשמירת תו unicode בודד?
  6. מהו גודל של wchar_t? (למתכנתי C++/C)‏?
  7. מהי גודל של תו unicode בשפת התכנות שאתה אוהב?
  8. מהו קידוד של מחרוזת (unicode) בשפת התכנות/toolkit שאתה אוהב?
  9. מה הבדל בין utf-8,‏ utf-16, ‏utf-32?‏
  10. מהו אורך התו הארוך ביותר ב־utf8?‏
  11. בניתי ספרייה עם שתי פונקציות בלבד. מה לא בסדר בקוד הבא (שתי בעיות לפחות)? extern Char32 to_upper_char(Char32); void to_upper(String str) { for(int i=0;i<str.size();i++) str[i]=to_upper(str[i]); }
  12. מה לא בסדר בקוד הבא: def print_error_message(message): print translate("Error occured: ")+"“"+translate(message)+"”"
  13. מה לא בסדר בקוד הזה: <?php $rtl_langs=array("he","ar","pa"); ?> ...
    > ...
  14. אני רוצה לחתוך את הטקסט בצורה יפה, כך שזה לא ייחתך באמצע המילה: מה לא בסדר בקוד הבא: // Cut nice pice of text wstring cut_nicely(wstring const &orig,int n) { if(orig.size()<=n) return orig; return orig.substr(orig.find_first_of(L" \r\n\t\f",n)); }

תשובות

  1. מה שחסר זה קידוד: האם זה utf-8, האם זה cp1255?
  2. ב־C זה תלוי בסביבה: ב־unix בד"כ wchar_t מייצג נקודת unicode בודדת לכן זה נכון (בהסתייגות מסוימת). ב־windows היום זה לא נכון כי wchar_t מייצג תוי utf-16 בעלי גודל במשתנה. ב־Java זה לא נכון גם כן כי הייצוג הפנימי של מחרוזת ב־Java זה utf-16.

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

  3. באנגלית יש רק צורת רבים אחת, בשפות אחרות יש יותר מצורה אחת, אפילו בעברית יש לפחות שתי צורות רבים, למשל:יומיים, 3 ימים. לכן הקוד הזה בנוי בהנחה לא נכונה לגבי שפות אחרות.
  4. ייצוג dd/mm/yyyy הוא נכון בישראל אבל אינו נכוו בארה"ב. צריך להגדיר locale ע"י setlocale(LC_ALL,"")‎ ואז להשתמש ב־‎%c שמותאם ל־locale.
  5. 32 ביט. ליתר דיוק טווח ערכי ה־unicode הוא 0x0000 עד 0x10FFFF שזה דורש יותר משני בתים!.
  6. תלוי במערכת הפעלה: ב־windows זה 16 ביט במרבית מערכות דמויות unix זה 32 ביט.
  7. טוב פה אתם צריכים לענות. כמה דוגמאות: Python זה שני בתים, Java כנ"ל. ב־C++/C ב־unix זה 32 ביט.
  8. ב־Java 1.5 ומעלה זה utf-16 ב־Python זה ucs-16 (לא תומך בכל הטווח). ב־glib זה utf-8/utf-32 או בהתאם ל־locale ב־qt3 זה ucs-16, ב־qt4 זה utf-16 ב־ICU זה־utf-16.‏
  9. utf-8 זה קידוד של 8 ביטים בעלי תוים באורך משתנה (מבית 1 עד ל־4 בתים). הוא תואם לאחור את ASCII.‏ utf-16 שזהו קידוד של 16 ביטים בעל אורך משתנה אחד או שניים תוים (קרי 2 או 4 בתים). utf-32 זהו קידוד בעל אורך קבוע בו כל תו מייצג נקודת unicode.
  10. בניגוד לדעות שונות אורך התו הגדול ביותר ב־utf-8 תקני הוא רק 4 בתים. בגלל שטווח unicode נגמר ב־10FFFF. לכן כל צירופים ארוכים יותר לא מהווים קידוד נכון.
  11. בעיה ראשונה: המרה יכולה לשנות מספר תווים. לדוגמה: ß הופכת ל־SS, בנוסף היא יכולה להיות תלויה בהקשר כמו מיקום התו במילה ועוד.
    בעיה שניה: ההמרה תלויה ב־locale, למשל בטורקית תו "i" הופך ל־"İ" ולא ל־"I"‏.
  12. מרכאות "“" ו־"”" הן ספציפיות לאנגלית בעברית משתמשים באותו תו " ברוסית ב־"«" וב־"»". צריך תרגם:
    `translate('Error "%s"') % translate(message);‎
  13. זהו קוד חמדני. מה עם יידיש? מה עם עוד מספר שפות שנשכחו שנכתבות מימין לשמאל. כיום אין ב־locale סימון לזה, אבל ניתן להשתמש בפתרון כמו ב־KDE בו תרגום "LTR" ל־"RTL" מסמן כיווניות השפה, מה שמאפשר גמישות למתרגם לבחור את הכיווניות גם אם המתכנת שכח.
  14. זה לא יעבוד עבור סינית, כי הכתיבה שם ללא רווחים, כי כל מילה היא תו בודד.

סיכום

  1. אם הצלחת לענות על כל 14 השאלות... מזל טוב, אתה יודע דבר או שניים בבנאום ולוקליזציה.
  2. אם ענית על 2/3 מהשאלות. יפה, אבל זה לא מספיק.
  3. אם ענית על פחות מחצי השאלות... תתחיל לפתוח ספרים.
  4. אם ענית על פחות מ־1/4 השאלות, אסור לך לכתוב קוד עבור בני אדם, רק עבור מכונות.

תגובות

ik_5, ב־19.4.2009, 11:59

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

ארתיום, ב־19.4.2009, 12:07

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

ממש לא. אני שואל שאלות ככליות לא קשורות לטכנולוגיית הפיתוח. למשל, קוד ה־python לבחירת single/plural form הוא כלל לא קשור לטכנולוגיה: אותה שגיאה תהיה ב־Pascal ב־Java ב־Qt או Win32 API.

מה שאני כותב... מה אתה צריך לדעת על טכנולוגיות באופן כללי כדי לכתוב נכון תכנה שמוכנה ל־i18n ו־i10n.

שי, ב־19.4.2009, 20:43

שאלה 11:
התשובה השניה שלך מוזרה; לא הראית לנו את to_upper_char, אבל אתה רוצה שנסיק שהיא לא משתמשת ב-locale. מצד שני, הקוד של to_upper קורא ל-to_upper (במקום to_upper_char) על כל תו.

שאלה 12:
השימוש באותו תו (") משני צדדיו של ציטוט הוא חדש בעברית, ובמידה רבה תוצאה של שימוש במחשבים. עד לפני 20-30 שנה, היו משתמשים בד"כ במרכאות תחתיות בתחילת הציטוט: ,,משהו כזה''. עדיין אפשר לראות את זה לפעמים.

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

ארתיום, ב־19.4.2009, 23:00

אבל אתה רוצה שנסיק שהיא לא משתמשת ב-locale.

אתה צודק. יותר מזה, ב־C‏ toupper דווקא כן משתמשת ב־locale שמוגדר גלובלית לתהליך ע"י setlocale. אבל, רוב הספריות הקיימות לפעולה כזו, כמו: boost::to_upper של boost, ‏String.toUpper של Java וגם UnicodeString::toUpper של ICU, מקבלות גם locale כפרמטר אופציונלי. הרעיון הוא שצריך לזכור שפעולות רבות מסוג זה, הן לא רק תלויות בקידוד הגולמי של הטקסט אלא גם ב־locale עצמו.

השימוש באותו תו (") משני צדדיו של ציטוט הוא חדש בעברית

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

אגב, אחת הבעיות של מרכאות חכמות זה גם לדעת לא לדחוף אותם לכל מקום. כמה פעמים ראית מסמכי word בעברית עם מרכאות מאנגלית? אחד הטלאים שהכנסתי בזמנו ל־WordPress בעברית היה ביטול מרכאות חכמות. חשוב לשים לזה לב.

דווקא בסינית זה כן יעבוד

תשים לב איפה ניסיתי לחתוך --- בסימני רווח הרגילים, כמו שורה חדשה, רווח או ‎\t. הם פשוט אינם בסינית. תפתח לדוגמה דף ויקיפדיה בסינית ותסתכל בו, אתה תראה שאין שם רווחים בכלל, אפילו לא אחרי סימני פיסוק.

לכן הקוד מותאם לשפה מערבית בלבד. כדי לעשות את הפעולה צריך להשתמש למשל ב־BreakIterator של ICU או ב־QTextBoundaryFinder של Qt4 שיודע למצוא מקומות חיתוך בצורה נכונה.

שי, ב־20.4.2009, 2:33

דווקא בסינית זה כן יעבוד

תשים לב איפה ניסיתי לחתוך — בסימני רווח הרגילים

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

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

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

עידו, ב־20.4.2009, 15:43

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

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

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

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

בכל מקרה, שני קישורים טובים לכל מי שמתעניין בנושא של בינאום תוכנה:

  • אתר של MSDN שמסביר את הנושאים הבעייתיים, מראה קטעי קוד (ב dot Net) ודוגמאות של מקרים בעתיים מהעולם

http://msdn.microsoft.com/en-us/goglobal/bb688110.aspx

  • ה CLDR הוא דאטהבייס של פורמטים שונים הנחוצים ל I18N - דברים כמו פורמטים של תאריך, שעה, מטבע וכו' לפי Locale.

http://cldr.unicode.org/

הוסף תגובה:

 
 כתובת דוא"ל לא תוצג
 

ניתן לכתוב תגובות עם שימוש בתחביר Markdown.

חובה לאפשר JavaScript כדי להגיב.

דפים

נושאים