-
Notifications
You must be signed in to change notification settings - Fork 13.9k
feat(i18n): add Persian (fa) locale with Jalali-aware date formatting #29495
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ | |
| "es-419", | ||
| "eu", | ||
| "et", | ||
| "fa", | ||
| "fi", | ||
| "fr", | ||
| "he", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| { | ||
| "today": "امروز", | ||
| "view_previous_month": "مشاهده ماه قبل", | ||
| "view_next_month": "مشاهده ماه بعد", | ||
| "select": "انتخاب کنید...", | ||
| "save": "ذخیره", | ||
| "cancel": "لغو", | ||
| "back": "بازگشت", | ||
| "next": "بعدی", | ||
| "date": "تاریخ", | ||
| "time": "زمان", | ||
| "timezone": "منطقه زمانی", | ||
| "confirm": "تأیید", | ||
| "loading": "در حال بارگذاری...", | ||
| "duration": "مدت زمان", | ||
| "no_available_slots": "زمان خالی موجود نیست", | ||
| "reschedule": "زمانبندی مجدد", | ||
| "required": "الزامی", | ||
| "submit": "ارسال", | ||
| "continue": "ادامه", | ||
| "close": "بستن", | ||
| "search": "جستجو", | ||
| "settings": "تنظیمات", | ||
| "general": "عمومی", | ||
| "language": "زبان", | ||
| "availability": "در دسترس بودن", | ||
| "event_type": "نوع رویداد", | ||
| "bookings": "رزروها", | ||
| "upcoming": "آینده", | ||
| "past": "گذشته", | ||
| "minute_timeUnit": "دقیقه" | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,7 +1,19 @@ | ||||||||||||||||||
| import { getWeekdayNames, formatWeekday } from "./dateTimeFormatter"; | ||||||||||||||||||
| import { formatWeekday, getWeekdayNames, isPersianCalendarLocale } from "./dateTimeFormatter"; | ||||||||||||||||||
|
|
||||||||||||||||||
| type WeekdayFormat = "short" | "long"; | ||||||||||||||||||
|
|
||||||||||||||||||
| const SATURDAY = 6; | ||||||||||||||||||
| const SUNDAY = 0; | ||||||||||||||||||
|
|
||||||||||||||||||
| /** | ||||||||||||||||||
| * Default first day of the week for a locale when the user has not set one explicitly. | ||||||||||||||||||
| * Persian (Shamsi) calendars start the week on Saturday; everything else defaults to Sunday. | ||||||||||||||||||
| */ | ||||||||||||||||||
| export function getWeekStartForLocale(locale: string | string[] | undefined): 0 | 6 { | ||||||||||||||||||
| const normalizedLocale = Array.isArray(locale) ? locale[0] : locale || "en"; | ||||||||||||||||||
| return isPersianCalendarLocale(normalizedLocale) ? SATURDAY : SUNDAY; | ||||||||||||||||||
| } | ||||||||||||||||||
|
Comment on lines
+12
to
+15
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider handling empty array input defensively. If an empty array is passed, While empty arrays may be unlikely in practice, adding a fallback would make the function more robust and consistent. 🛡️ Proposed defensive fix export function getWeekStartForLocale(locale: string | string[] | undefined): 0 | 6 {
- const normalizedLocale = Array.isArray(locale) ? locale[0] : locale || "en";
+ const normalizedLocale = Array.isArray(locale) ? (locale[0] || "en") : (locale || "en");
return isPersianCalendarLocale(normalizedLocale) ? SATURDAY : SUNDAY;
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
|
|
||||||||||||||||||
| export function weekdayNames(locale: string | string[], weekStart = 0, format: WeekdayFormat = "long") { | ||||||||||||||||||
| const normalizedLocale = Array.isArray(locale) ? locale[0] : locale || "en"; | ||||||||||||||||||
| return getWeekdayNames(normalizedLocale, weekStart, format); | ||||||||||||||||||
|
|
||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
Does Intl.DateTimeFormat support thecalendaroption (e.g. calendar: "persian") and in which engine/Node versions?💡 Result:
Yes, the Intl.DateTimeFormat constructor supports the calendar option [1][2][3]. You can specify a calendar, such as "persian", "chinese", or "gregory", in the options object [1][2]. This option can also be set via the Unicode extension key "ca" in the locale string (e.g., "en-u-ca-persian") [1][3]. When both are provided, the options property takes precedence [1][3]. Regarding support: - Engines: The calendar option is part of the standard ECMAScript Internationalization API (ECMA-402) and is supported in all modern web browsers and server-side JavaScript environments that include ICU (International Components for Unicode) data [1][2][4][5]. - Node.js Versions: Support for this feature in Node.js depends on the version of the ICU library bundled with the Node.js release [4][5]. Since Node.js 13+, the default builds include full ICU data, which supports these calendar types [4][5]. If you are using a custom build or an older version of Node.js, you may need to ensure full-icu or system-icu data is available to support non-Gregorian calendars; otherwise, it may fall back to default behavior [4][5]. Important Note: When using the calendar option, keep in mind that the calendar must be supported for the specified locale. If you pass an invalid or unsupported calendar, the environment typically ignores it or reverts to the locale's default calendar [6][7][8]. You can verify which calendars are supported for a given environment using Intl.supportedValuesOf('calendar') [1][3]. Additionally, third-party polyfills, such as those found in some older versions of FormatJS, might not implement full non-Gregorian calendar support [9].
Citations:
🏁 Script executed:
Repository: calcom/cal.diy
Length of output: 102
🏁 Script executed:
Repository: calcom/cal.diy
Length of output: 48398
🏁 Script executed:
Repository: calcom/cal.diy
Length of output: 9464
🏁 Script executed:
Repository: calcom/cal.diy
Length of output: 672
🏁 Script executed:
Repository: calcom/cal.diy
Length of output: 281
Fix resolveFormatterLocale to avoid generating invalid BCP-47 tags (duplicate
-u-)resolveFormatterLocaleappends-u-ca-persianwhenever the locale is Persian perisPersianCalendarLocaleand doesn’t already contain-ca-. For inputs likefa-u-nu-latn, it producesfa-u-nu-latn-u-ca-persian, which throwsRangeError: Invalid language tagand breaks Intl-based formatting for that locale.A more robust approach is to set the calendar via
Intl.DateTimeFormatoptions (calendar: "persian") instead of mutating the locale string.🛡️ Minimal hardening of the string approach
function resolveFormatterLocale(locale: string): string { - if (isPersianCalendarLocale(locale) && !locale.toLowerCase().includes("-ca-")) { - return `${locale}-u-ca-persian`; - } - return locale; + const lower = locale.toLowerCase(); + if (isPersianCalendarLocale(locale) && !lower.includes("-ca-")) { + return lower.includes("-u-") ? `${locale}-ca-persian` : `${locale}-u-ca-persian`; + } + return locale; }🤖 Prompt for AI Agents