feat: add Jalali (Persian/Shamsi) calendar support with Saturday as week start (#490)

* feat: add Jalali (Persian/Shamsi) calendar support with Saturday as week start

- Add jalaali-js library for Gregorian ↔ Jalali date conversion
- Create lib/jalali-utils.ts with Jalali calendar utilities
- Create hooks/use-calendar-locale.ts for unified calendar locale handling
- Expand FirstDayOfWeek type to include 6 (Saturday)
- Update all calendar views (month, week, day, mini, toolbar) to support
  Jalali calendar display and Saturday-first week ordering
- Add Jalali month names (Farvardin … Esfand) to all locale files
- Add Persian (fa) locale with full translations
- Update settings UI to include Saturday as first day of week option
- Update useFormatEventDate to show Jalali dates when locale is fa
- Auto-detect Jalali calendar when fa locale is active

The calendar system automatically switches to Jalali when the locale is
set to Persian (fa). All internal date handling remains Gregorian (ISO
8601) for JMAP protocol compatibility; Jalali conversion is purely at
the display layer.

* Add PR template for Jalali calendar feature

* chore: remove accidentally added PR template

* fix: add image_too_large key to fa locale for PR #462 compatibility
This commit is contained in:
Hamed Fallah
2026-07-08 15:42:42 +02:00
committed by GitHub
parent 3d36492518
commit e10fced28a
31 changed files with 798 additions and 118 deletions
+13 -1
View File
@@ -2904,7 +2904,19 @@
"sep": "zář",
"oct": "říj",
"nov": "lis",
"dec": "pro"
"dec": "pro",
"far": "Farvardin",
"ord": "Ordibehesht",
"kho": "Khordad",
"tir": "Tir",
"mor": "Mordad",
"sha": "Shahrivar",
"meh": "Mehr",
"aba": "Aban",
"aza": "Azar",
"dey": "Dey",
"bah": "Bahman",
"esf": "Esfand"
},
"nav_open_menu": "Otevřít nabídku"
},
+13 -1
View File
@@ -2723,7 +2723,19 @@
"sep": "Sep",
"oct": "Okt",
"nov": "Nov",
"dec": "Dec"
"dec": "Dec",
"far": "Farvardin",
"ord": "Ordibehesht",
"kho": "Khordad",
"tir": "Tir",
"mor": "Mordad",
"sha": "Shahrivar",
"meh": "Mehr",
"aba": "Aban",
"aza": "Azar",
"dey": "Dey",
"bah": "Bahman",
"esf": "Esfand"
},
"notifications": {
"event_created": "Begivenhed oprettet",
+13 -1
View File
@@ -2904,7 +2904,19 @@
"sep": "Sep",
"oct": "Okt",
"nov": "Nov",
"dec": "Dez"
"dec": "Dez",
"far": "Farvardin",
"ord": "Ordibehesht",
"kho": "Khordad",
"tir": "Tir",
"mor": "Mordad",
"sha": "Shahrivar",
"meh": "Mehr",
"aba": "Aban",
"aza": "Azar",
"dey": "Dey",
"bah": "Bahman",
"esf": "Esfand"
},
"nav_open_menu": "Menü öffnen"
},
+13 -1
View File
@@ -2723,7 +2723,19 @@
"sep": "Sep",
"oct": "Oct",
"nov": "Nov",
"dec": "Dec"
"dec": "Dec",
"far": "Farvardin",
"ord": "Ordibehesht",
"kho": "Khordad",
"tir": "Tir",
"mor": "Mordad",
"sha": "Shahrivar",
"meh": "Mehr",
"aba": "Aban",
"aza": "Azar",
"dey": "Dey",
"bah": "Bahman",
"esf": "Esfand"
},
"notifications": {
"event_created": "Event created",
+13 -1
View File
@@ -2904,7 +2904,19 @@
"sep": "Sep",
"oct": "Oct",
"nov": "Nov",
"dec": "Dic"
"dec": "Dic",
"far": "Farvardin",
"ord": "Ordibehesht",
"kho": "Khordad",
"tir": "Tir",
"mor": "Mordad",
"sha": "Shahrivar",
"meh": "Mehr",
"aba": "Aban",
"aza": "Azar",
"dey": "Dey",
"bah": "Bahman",
"esf": "Esfand"
},
"nav_open_menu": "Abrir menú"
},
+21 -3
View File
@@ -681,7 +681,12 @@
"recipient_email_placeholder": "آدرس ایمیل",
"recipient_name_placeholder": "نام نمایشی",
"autocomplete_search_server": "جستجو در سرور",
"autocomplete_searching": "در حال جستجو..."
"autocomplete_searching": "در حال جستجو...",
"text_direction": {
"toggle": "تغییر جهت متن (چپ‌به‌راست / راست‌به‌چپ)",
"ltr": "چپ‌به‌راست",
"rtl": "راست‌به‌چپ"
}
},
"confirm_dialog": {
"confirm": "تأیید",
@@ -1880,7 +1885,8 @@
"validation": {
"empty": "نام قالب الزامی است",
"too_long": "نام قالب باید ۲۰۰ کاراکتر یا کمتر باشد"
}
},
"image_too_large": "حجم تصویر بیش از حد مجاز (حداکثر ۱ مگابایت)"
},
"files": {
"display": {
@@ -2723,7 +2729,19 @@
"sep": "سپتامبر",
"oct": "اکتبر",
"nov": "نوامبر",
"dec": "دسامبر"
"dec": "دسامبر",
"far": "فروردین",
"ord": "اردیبهشت",
"kho": "خرداد",
"tir": "تیر",
"mor": "مرداد",
"sha": "شهریور",
"meh": "مهر",
"aba": "آبان",
"aza": "آذر",
"dey": "دی",
"bah": "بهمن",
"esf": "اسفند"
},
"notifications": {
"event_created": "رویداد ایجاد شد",
+13 -1
View File
@@ -2723,7 +2723,19 @@
"sep": "sept.",
"oct": "oct.",
"nov": "nov.",
"dec": "déc."
"dec": "déc.",
"far": "Farvardin",
"ord": "Ordibehesht",
"kho": "Khordad",
"tir": "Tir",
"mor": "Mordad",
"sha": "Shahrivar",
"meh": "Mehr",
"aba": "Aban",
"aza": "Azar",
"dey": "Dey",
"bah": "Bahman",
"esf": "Esfand"
},
"notifications": {
"event_created": "Événement créé",
+13 -1
View File
@@ -2723,7 +2723,19 @@
"sep": "Szep",
"oct": "Okt",
"nov": "Nov",
"dec": "Dec"
"dec": "Dec",
"far": "Farvardin",
"ord": "Ordibehesht",
"kho": "Khordad",
"tir": "Tir",
"mor": "Mordad",
"sha": "Shahrivar",
"meh": "Mehr",
"aba": "Aban",
"aza": "Azar",
"dey": "Dey",
"bah": "Bahman",
"esf": "Esfand"
},
"notifications": {
"event_created": "Esemény létrehozva",
+13 -1
View File
@@ -2904,7 +2904,19 @@
"sep": "set",
"oct": "ott",
"nov": "nov",
"dec": "dic"
"dec": "dic",
"far": "Farvardin",
"ord": "Ordibehesht",
"kho": "Khordad",
"tir": "Tir",
"mor": "Mordad",
"sha": "Shahrivar",
"meh": "Mehr",
"aba": "Aban",
"aza": "Azar",
"dey": "Dey",
"bah": "Bahman",
"esf": "Esfand"
},
"nav_open_menu": "Apri menu"
},
+13 -1
View File
@@ -2904,7 +2904,19 @@
"sep": "9月",
"oct": "10月",
"nov": "11月",
"dec": "12月"
"dec": "12月",
"far": "Farvardin",
"ord": "Ordibehesht",
"kho": "Khordad",
"tir": "Tir",
"mor": "Mordad",
"sha": "Shahrivar",
"meh": "Mehr",
"aba": "Aban",
"aza": "Azar",
"dey": "Dey",
"bah": "Bahman",
"esf": "Esfand"
},
"nav_open_menu": "メニューを開く"
},
+13 -1
View File
@@ -2904,7 +2904,19 @@
"sep": "9월",
"oct": "10월",
"nov": "11월",
"dec": "12월"
"dec": "12월",
"far": "Farvardin",
"ord": "Ordibehesht",
"kho": "Khordad",
"tir": "Tir",
"mor": "Mordad",
"sha": "Shahrivar",
"meh": "Mehr",
"aba": "Aban",
"aza": "Azar",
"dey": "Dey",
"bah": "Bahman",
"esf": "Esfand"
},
"nav_open_menu": "메뉴 열기"
},
+13 -1
View File
@@ -2904,7 +2904,19 @@
"sep": "sept.",
"oct": "okt.",
"nov": "nov.",
"dec": "dec."
"dec": "dec.",
"far": "Farvardin",
"ord": "Ordibehesht",
"kho": "Khordad",
"tir": "Tir",
"mor": "Mordad",
"sha": "Shahrivar",
"meh": "Mehr",
"aba": "Aban",
"aza": "Azar",
"dey": "Dey",
"bah": "Bahman",
"esf": "Esfand"
},
"nav_open_menu": "Atvērt izvēlni"
},
+13 -1
View File
@@ -2904,7 +2904,19 @@
"sep": "sep",
"oct": "okt",
"nov": "nov",
"dec": "dec"
"dec": "dec",
"far": "Farvardin",
"ord": "Ordibehesht",
"kho": "Khordad",
"tir": "Tir",
"mor": "Mordad",
"sha": "Shahrivar",
"meh": "Mehr",
"aba": "Aban",
"aza": "Azar",
"dey": "Dey",
"bah": "Bahman",
"esf": "Esfand"
},
"nav_open_menu": "Menu openen"
},
+13 -1
View File
@@ -2904,7 +2904,19 @@
"sep": "wrz",
"oct": "paź",
"nov": "lis",
"dec": "gru"
"dec": "gru",
"far": "Farvardin",
"ord": "Ordibehesht",
"kho": "Khordad",
"tir": "Tir",
"mor": "Mordad",
"sha": "Shahrivar",
"meh": "Mehr",
"aba": "Aban",
"aza": "Azar",
"dey": "Dey",
"bah": "Bahman",
"esf": "Esfand"
},
"nav_open_menu": "Otwórz menu"
},
+13 -1
View File
@@ -2723,7 +2723,19 @@
"sep": "set",
"oct": "out",
"nov": "nov",
"dec": "dez"
"dec": "dez",
"far": "Farvardin",
"ord": "Ordibehesht",
"kho": "Khordad",
"tir": "Tir",
"mor": "Mordad",
"sha": "Shahrivar",
"meh": "Mehr",
"aba": "Aban",
"aza": "Azar",
"dey": "Dey",
"bah": "Bahman",
"esf": "Esfand"
},
"notifications": {
"event_created": "Evento criado",
+13 -1
View File
@@ -2723,7 +2723,19 @@
"sep": "Sep",
"oct": "Oct",
"nov": "Nov",
"dec": "Dec"
"dec": "Dec",
"far": "Farvardin",
"ord": "Ordibehesht",
"kho": "Khordad",
"tir": "Tir",
"mor": "Mordad",
"sha": "Shahrivar",
"meh": "Mehr",
"aba": "Aban",
"aza": "Azar",
"dey": "Dey",
"bah": "Bahman",
"esf": "Esfand"
},
"notifications": {
"event_created": "Eveniment creat",
+13 -1
View File
@@ -2904,7 +2904,19 @@
"sep": "сент.",
"oct": "окт.",
"nov": "нояб.",
"dec": "дек."
"dec": "дек.",
"far": "Farvardin",
"ord": "Ordibehesht",
"kho": "Khordad",
"tir": "Tir",
"mor": "Mordad",
"sha": "Shahrivar",
"meh": "Mehr",
"aba": "Aban",
"aza": "Azar",
"dey": "Dey",
"bah": "Bahman",
"esf": "Esfand"
},
"nav_open_menu": "Открыть меню"
},
+13 -1
View File
@@ -2723,7 +2723,19 @@
"sep": "Eyl",
"oct": "Eki",
"nov": "Kas",
"dec": "Ara"
"dec": "Ara",
"far": "Farvardin",
"ord": "Ordibehesht",
"kho": "Khordad",
"tir": "Tir",
"mor": "Mordad",
"sha": "Shahrivar",
"meh": "Mehr",
"aba": "Aban",
"aza": "Azar",
"dey": "Dey",
"bah": "Bahman",
"esf": "Esfand"
},
"notifications": {
"event_created": "Etkinlik oluşturuldu",
+13 -1
View File
@@ -2904,7 +2904,19 @@
"sep": "верес.",
"oct": "жовт.",
"nov": "лист.",
"dec": "груд."
"dec": "груд.",
"far": "Farvardin",
"ord": "Ordibehesht",
"kho": "Khordad",
"tir": "Tir",
"mor": "Mordad",
"sha": "Shahrivar",
"meh": "Mehr",
"aba": "Aban",
"aza": "Azar",
"dey": "Dey",
"bah": "Bahman",
"esf": "Esfand"
},
"nav_open_menu": "Відкрити меню"
},
+13 -1
View File
@@ -2904,7 +2904,19 @@
"sep": "9月",
"oct": "10月",
"nov": "11月",
"dec": "12月"
"dec": "12月",
"far": "Farvardin",
"ord": "Ordibehesht",
"kho": "Khordad",
"tir": "Tir",
"mor": "Mordad",
"sha": "Shahrivar",
"meh": "Mehr",
"aba": "Aban",
"aza": "Azar",
"dey": "Dey",
"bah": "Bahman",
"esf": "Esfand"
},
"nav_open_menu": "打开菜单"
},