About
Jalali Calendar
The Iranian Solar Hijri calendar for Noctalia - on the bar, in a panel, and in the Control Center. It shows Iranian public holidays and civil occasions, with the Gregorian and Lunar Hijri dates alongside every day, and works entirely offline.
![]()

Plugin
| Field | Value |
|---|---|
| ID | borderliner/jalali_calendar |
| Entries | Bar widget: date; panel: calendar; shortcut: open |
Requirements
None. The plugin is self-contained: no external commands, no network access, and no files written.
Usage
None of the three surfaces is placed automatically.
Bar widget - Settings > Bar, add Jalali Calendar. It shows today's date. Clicking it opens the panel; the tooltip carries the long date, the Gregorian and Hijri equivalents, and the day's occasions.
Panel - a month grid with today highlighted, Fridays and public holidays in red, and the selected day's occasions listed underneath with its Gregorian and Hijri dates spelled out. Clicking the month title opens a month picker, and clicking the year there opens a year picker, so any date is two clicks away. Open it directly with:
noctalia msg panel-toggle borderliner/jalali_calendar:calendar
Control Center tile - Settings > Control Center > Home, add it to the
shortcuts. There are six slots, so one of the defaults has to give way. The
tile is labelled with today's date and opens the panel. In settings.toml the
same thing is:
[[control_center.shortcuts]]
type = "borderliner/jalali_calendar:open"
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
language |
select |
persian |
Script for month and weekday names, and the direction of the grid. |
digits |
select |
persian |
Numerals: Persian or Latin. |
week_start |
select |
saturday |
Which weekday the grid starts on. Saturday matches the Iranian week. |
scale |
double |
1.0 |
Scales the panel's text and cells, 0.7-2.0. Panel size is fixed by the manifest, so large values scroll rather than grow. |
events_national |
bool |
true |
Iranian civil, cultural and scientific days (non-holidays). |
events_ancient |
bool |
true |
Pre-Islamic and Zoroastrian festivals: Mehregan, Sadeh, Yalda and the rest. |
events_religious |
bool |
false |
Shia and Islamic observances (non-holidays). |
events_afghan |
bool |
false |
Afghan national days. Their public holidays only count as days off while this is on. |
show_hijri |
bool |
true |
Lunar Hijri day number in each cell. |
show_gregorian |
bool |
true |
Gregorian day number in each cell. |
show_holidays |
bool |
true |
Mark public holidays and list each day's occasions. |
format |
string |
{weekday} {day} {month} |
Bar widget template. Placeholders: {day} {month} {year} {weekday}. |
font_size |
int |
0 |
Bar widget font size in pixels. 0 follows the bar's own size. |
show_glyph |
bool |
true |
Show an icon beside the date on the bar. |
glyph |
glyph |
calendar |
Which icon. |
Notes
No network, no writes, no processes
dependencies = []. The plugin makes no HTTP requests, spawns no commands and
writes no files. Everything it needs is in the plugin directory.
Where the calendar data comes from
Two public-domain (CC0 1.0) datasets, compiled into occasions.luau at build
time by tools/generate_occasions.py:
- Occasions and holidays - persian-calendar/events, whose Iranian entries transcribe the official University of Tehran calendar.
- Lunar Hijri month starts - roozbehp/qamari, the first Gregorian day of each Hijri month as actually observed in Iran.
Occasions are stored by rule rather than resolved per year, so the table stays small and stays correct for any year you browse to.
Which occasions are included
Occasions come in four groups, each switchable in the settings: Iranian civil and cultural days, ancient Iranian festivals, religious observances, and Afghan national days. Civil days and ancient festivals are on by default.
Iranian public holidays are always shown, by name, whatever is switched on, so the calendar always tells you which days are days off.
Titles are shown without honorifics such as «حضرت».
The grouping is keyword-based over the Persian titles, because the upstream
data ships no categories. The rules are in tools/generate_occasions.py.
How accurate the Hijri dates are
Iran fixes each lunar month's start by sighting and publishes about a year ahead, so the observed data stops at a horizon (currently 1447/10 = 2026-03-21). Past that the plugin continues the real sequence using tabular month lengths anchored to the last observed start, which stays close rather than drifting. Seven individual months are missing from the upstream source and are bridged between the surrounding anchors. Solar holidays fall on fixed Jalali dates and are always exact.
Fonts
Vazirmatn is bundled (fonts/, SIL OFL 1.1) and loaded at startup, because the
bar's configured font often has no Persian glyphs. If it fails to load the
plugin falls back to the host font rather than failing to render.
Limitations
- Occasion titles are Persian only. The source dataset carries no English translations, so Latin mode changes the numerals, month and weekday names and grid direction, but occasion names stay Persian.
- Seven weekday-relative occasions from the dataset ("the second Friday of Mehr") are not shown. None is a public holiday; the generator lists them when it runs.
- Per-day tooltips are not used: they need plugin API 32 and this plugin targets 22. Click a day to see its occasions.
Development
The date logic is pure and runs headlessly, so it is tested without a shell:
./check.sh # luau-analyze, tests, manifest lint, store limits
luau tests/run.luau # tests alone
Two host details worth knowing before editing:
requireneeds the.luauextension in Noctalia, and Noctalia resolves paths relative to the plugin root. The standaloneluauCLI used by the tests wants the opposite. Shared modules use a smallreq()helper that tries both; entry scripts, which only run inside Noctalia, use the extension directly. It is also why every module sits at the plugin root.- Neither
ui.buttonnorui.inputaccepts afontFamily- onlyui.labeldoes. Text buttons are therefore clickable columns wrapping a label, and the year picker is a grid rather than a text field.
License
MIT - see LICENSE. The bundled occasion and month-start data is CC0 1.0
(public domain) and is credited above. The bundled Vazirmatn font is SIL Open
Font License 1.1 - see fonts/LICENSE-Vazirmatn.txt.
Versions
| Version | Plugin API | Updated |
|---|---|---|
| v1.0.1 latest | 22 | Sep 23, 2026 |
Older versions stay installable on a Noctalia release whose plugin API is below the latest version's.