שאלו את שאלתכם וקבלו סיכום של המסמך על ידי הפניה לדף הזה ולספק ה-AI שתבחרו
התוכן של הדף הזה תורגם באמצעות AI.
צפייה בגרסה האחרונה של התוכן המקורי באנגליתאם יש לכם רעיון לשיפור התיעוד הזה, אל תהססו לתרום על ידי הגשת Pull Request ב-GitHub.
קישור GitHub לתיעודהעתקת ה-Markdown של המסמך ללוח
עיצוב תאריכים ומספרים לפי שפה ואזור (Locale) באמצעות Intl
תרגום מחרוזות הוא החצי הגלוי של i18n. החצי שמייצר את מרבית דיווחי הבאגים הוא העיצוב (formatting): משתמש גרמני שרואה 1,234.56 במקום 1.234,56, משתמש יפני שרואה 08/02/2026 וקורא זאת כחודש אוגוסט, או תאריך שמרונדר באופן שונה בשרת ובדפדפן ומפיל את העמוד במהלך ה-hydration.
אף אחד מהמקרים הללו אינו דורש ספרייה חיצונית. ה-API המובנה Intl קיים בכל סביבת ריצה מודרנית.
תוכן העניינים
התחילו במחיקת פונקציית העזר (Helper) לתאריכים שלכם
כמעט בכל בסיס קוד יש פונקציית formatDate שנכתבה עוד לפני שמישהו חשב על locales. היא מקבעת סדר מסוים, תו מפריד, ובדרך כלל שמות חודשים באנגלית.
העתקת הקוד ללוח
האובייקט Intl.DateTimeFormat מחליף אותה ופועל נכון בכל locale:
העתקת הקוד ללוח
אותו עיקרון חל גם על מספרים. הקריאה toFixed(2) מייצרת 1234.56 בכל מקום, וזה שגוי ברוב מדינות אירופה.
מה ש-Intl מכסה
פתיחת הטבלה בחלון כדי לראות את כל תוכן הנתונים בבירור
| API | שימוש עיקרי |
|---|---|
Intl.DateTimeFormat | תאריכים וזמנים, עם הגדרות מוכנות מראש של dateStyle / timeStyle |
Intl.NumberFormat | מספרים עשרוניים, מטבעות, אחוזים, יחידות מידה, רישום מקוצר (compact notation) |
Intl.RelativeTimeFormat | "לפני 3 ימים", "בעוד שעתיים" |
Intl.ListFormat | חיבור רשימות ("א, ב ו-ג" לעומת "a, b, and c") |
Intl.PluralRules | לאיזו קטגוריית ריבוי (plural category) שייך מספר נתון |
Intl.Collator | מיון מחרוזות בצורה נכונה לפי חוקי השפה |
האובייקט Intl.Collator הוא הרכיב שרבים נוטים לשכוח. הפעלת array.sort() על מחרוזות משתמשת בסדר ה-code points, כך שתווים עם ניקוד או הטעמות ממוינים אחרי z, והאות השוודית ö מגיעה למקום הלא נכון. אם אתם ממיינים רשימות המוצגות למשתמש, מיינו תמיד באמצעות collator.
העתקת הקוד ללוח
העדיפו הגדרות מוכנות מראש (Presets) על פני אפשרויות בהתאמה ידנית
המאפיינים dateStyle ו-timeStyle מאפשרים ל-locale לקבוע את הסדר ואת התווים המפרידים. פירוט ידני של year, month ו-day מעניק שליטה שבדרך כלל מוטב להימנע ממנה, מכיוון שהסדר הנכון משתנה לפי אזור ושפה, ובהגדרה ידנית אתם דורסים את נתוני CLDR על סמך הנחות משלכם.
העתקת הקוד ללוח
השתמשו ברכיבים מפורשים רק כאשר העיצוב מחייב מבנה קבוע באופן מהותי, לדוגמה עמודת טבלה שחייבת להישאר צרה.
יצירת formatters היא פעולה יקרה
זהו פרט הביצועים החשוב ביותר. בניית מופע של Intl.NumberFormat כוללת טעינה של נתוני locale, והיא יקרה משמעותית מקריאת ה-format() שבאה בעקבותיה. ביצוע פעולה זו בתוך פונקציית רינדור או בלולאה הרצה על פני אלף שורות גורר עלות מורגשת.
העתקת הקוד ללוח
פונקציות כמו toLocaleDateString() ו-toLocaleString() מסתירות בתוכן את אותה הבעיה בדיוק: כל קריאה בונה מופע חדש של formatter. הן מתאימות לערך בודד אך שגויות לחלוטין בעיבוד רשימות.
בצעו שמירה במטמון (Cache) לפי השילוב של ה-locale וה-options, שכן הם אלה המגדירים את ה-formatter:
העתקת הקוד ללוח
באג אזור הזמן (Timezone) שמופיע רק ב-Production
באג זה עלול לבזבז שעות עבודה רבות. השרת מרנדר תאריך, הדפדפן מבצע hydration, ו-React זורק שגיאת hydration mismatch מכיוון ששני הצדדים ייצרו טקסט שונה.
הסיבה לכך היא ש-Intl.DateTimeFormat משתמש באזור הזמן המקומי של הסביבה כאשר לא מוגדר אזור זמן במפורש. שרת ה-production שלכם פועל ב-UTC, בעוד שהמחשב הנייד שלכם אינו כזה. כתוצאה מכך, הבאג בלתי נראה בסביבה המקומית ומשתחזר אך ורק ב-production, שזהו התרחיש הגרוע ביותר.
העתקת הקוד ללוח
קיימות שלוש גישות ישימות לפתרון:
- קיבוע אזור זמן (Pin a timezone) בשרת והעברתו במפורש. פתרון נכון ודטרמיניסטי, אך כל המשתמשים יראו UTC.
- רינדור בצד הלקוח בלבד (Client-only render), עם placeholder יציב עבור מעבר השרת. מדויק עבור כל משתמש, אך גורר הבהוב קל (flash).
- שמירת אזור הזמן של המשתמש והעברתו בשני הצדדים. התוצאה הטובה ביותר, אך דורשת את כמות העבודה הגדולה ביותר.
ללא קשר לגישה שתבחרו, העבירו תמיד timeZone במפורש עבור כל תאריך המרונדר הן בשרת והן בלקוח. תאריך ללא אזור זמן מוגדר הוא תאריך בעל שני ערכים שונים.
מטבע זקוק לקוד מטבע, לא לשפה ואזור (Locale)
שפה ואזור (locale) ומטבע הם שני דברים נפרדים לחלוטין. fr-FR אינו מעיד בהכרח על אירו: משתמש צרפתי יכול לצפות בחשבונית הנקובה בדולר ארה"ב (USD).
העתקת הקוד ללוח
ה-locale שולט בתווים המפרידים, בקיבוץ הספרות ובמיקום סימן המטבע. המטבע עצמו מגיע ישירות מהנתונים שלכם. גזירת האחד מהשני היא באג שמגיע עד למחלקת הנהלת חשבונות.
בנוסף, שימו לב למאפיין currencyDisplay. בממשק שבו מתקיימים מספר מטבעות במקביל, שימוש ב-"code" מונע בלבול בין דולר ארה"ב, דולר קנדי ודולר אוסטרלי.
זמן יחסי קריא וטבעי יותר מזמן מוחלט
עבור אירועים שהתרחשו לאחרונה, הניסוח "לפני שעתיים" עדיף בהרבה על חותמת זמן מוחלטת, והאובייקט Intl.RelativeTimeFormat מתאים אותו נכון לשפה.
העתקת הקוד ללוח
ההגדרה numeric: "auto" היא זו שמפיקה "אתמול" במקום "לפני יום 1". בלעדיה תקבלו את הצורה המספרית בכל שפה, מה שנראה מנוכר ומכני.
מה Intlayer מוסיף
Intlayer עוטף את המנגנונים הללו בפונקציות עזר מובנות עם מטמון (cache), כך שלא תצטרכו לנהל את ה-Map בעצמכם, וה-locale הפעיל מוחל כברירת מחדל במקום שתידרשו להעביר אותו בכל קריאה.
העתקת הקוד ללוח
הפונקציה date() מקבלת גם הגדרות מוכנות מראש ("short", "long", "dateOnly", "timeOnly", "full"), כך שהמקרים הנפוצים אינם דורשים אובייקט אפשרויות. קיימות גרסאות מקבילות עבור React ו-Vue כ-hooks וכ-composables, אשר מזהות את ה-locale הפעיל ישירות מתוך ה-context במקום לקבל אותו כארגומנט.
חשוב להבהיר מה מהות המנגנון: שכבת caching וברירת מחדל של locale מעל ה-API הסטנדרטי של הפלטפורמה. התנהגות העיצוב היא של Intl, וכל הנאמר במאמר זה תקף בין אם תבחרו להשתמש בה ובין אם לאו. חתימות מלאות זמינות ב-תיעוד פונקציות העיצוב (Formatters).
טעויות נפוצות
- שימוש ב-
toLocaleDateString()ללא ציון locale. הפונקציה תשתמש ב-locale של סביבת הריצה, אשר בשרת נקבע לפי הגדרות הקונטיינר. - עיצוב בתוך לולאה. עלות יצירת ה-formatter היא המרכיב הכבד ביותר. בנו אותו פעם אחת.
- היעדר
timeZoneבתאריכים איזומורפיים (Isomorphic). גורר שגיאת hydration mismatch שלעולם אינה משתחזרת במחשב המקומי שלכם. - גזירת מטבע מתוך ה-locale. השפה
fr-FRאינה בהכרח אירו. - הפעלת
sort()על מחרוזות המוצגות למשתמש. השתמשו ב-Intl.Collator. - קידוד ידני של שמות חודשים או ימים. הם כבר קיימים ב-CLDR בכל שפה.
- שימוש ב-
numeric: "always"עבור זמן יחסי. גורם להצגת "לפני יום 1" במקום שבו לכל שפה יש מילה ייעודית עבור "אתמול".
קריאה נוספת
תגובות
עדיין אין תגובות. היו הראשונים לשתף את מחשבותיכם.
