Hapio Calendar

Languages and right-to-left

Locales through Intl, the built-in English and Swedish strings, your own translations, rule descriptions and right-to-left layouts.

Two props make a calendar speak a language: locale formats every date and decides the defaults that depend on the region, and strings holds every text the calendar writes itself.

locale

A BCP 47 tag, 'en-US' by default (so a server render never depends on the server's settings). It drives:

  • every date, through Intl.DateTimeFormat and the formats: weekday and month names, the order of day and month, 12- or 24-hour times;
  • weekStartsOn: the region's first day of the week (Intl.Locale's week info, a built-in table where a browser lacks it, else Monday);
  • dir: right-to-left for languages written that way (Intl.Locale's text info).

Pass weekStartsOn (ISO: Monday is 1, Sunday is 7), dir or formats to choose differently.

strings

Every text the calendar writes itself, from "Today" to "Can't move it to …", is a member of CalendarStrings. English is built in; strings takes any part of it, merged over the English, and the functions among them (eventCount(n), moreLink(n), navigation(unit, period)) handle plurals and word order. The texts of the interaction engines (selecting, editing, placing, dropping, the scope dialog) are optional members whose English lives with the engine that uses them, so a read-only calendar does not carry them. The registry's own texts read strings too: the loading skeleton's name (loading) and the notice for a view kind the copied files cannot render (unsupportedView(label)).

Swedish

@hapio/calendar-core/locales/sv is a complete Swedish pack, every optional member included:

components/swedish-calendar.tsx
'use client'

import { sv } from '@hapio/calendar-core/locales/sv'
import type { CalendarEventInput, CalendarStrings } from '@hapio/calendar-react'

import { HapioCalendar } from '@/components/hapio-calendar/hapio-calendar'

// The Swedish pack, with one string of your own over it.
const strings: Partial<CalendarStrings> = { ...sv, noEvents: 'Inga bokningar den här veckan' }

export function SwedishCalendar({ events }: { events: readonly CalendarEventInput[] }) {
  return (
    <HapioCalendar
      events={events}
      locale="sv-SE" // dates, weekday names, Monday weeks, 24-hour times
      strings={strings} // every text the calendar writes, and the root's `lang` (`sv.lang`)
      timeZone="Europe/Stockholm"
      className="h-[36rem]"
    />
  )
}

Install @hapio/calendar-core (the same version as @hapio/calendar-react) to import it. Spread it and override what you need, as above.

The calendar's root carries lang, the language of its texts: the pack's lang (sv.lang is 'sv'; the built-in English says 'en'), never locale's alone, since locale only formats the dates. So do the parts the registry renders outside the root (the "+N more" popover, the actions menu, the reschedule form and the scope dialog), so screen readers pronounce a Swedish calendar's text as Swedish on an English page. A he-IL calendar without Hebrew strings says lang="en": its dates are Hebrew, its texts English. Set lang yourself only on your own text around the calendar.

An item that drags into calendars lives outside every calendar, so no calendar's strings reach its description ("Press Space to pick it up, then choose a time in the calendar"): pass the pack's text to it, <CalendarDraggable hint={sv.pickUpHint} … /> (or useExternalDraggable's hint). See Dragging in.

Other languages

Write a CalendarStrings of your own: TypeScript lists every member and the English default of each is in its documentation (API reference). Set its lang to the pack's language ('he'), which the calendar's root and popups then carry. Keep the pack in a module of its own and pass it with the matching locale, and give your draggable items its pickUpHint as above.

Rule descriptions

describeRule(rrule, locale) from @hapio/calendar-recurrence describes a recurrence rule in words: Swedish for a Swedish locale ("Varje vecka på måndagar och onsdagar"), English otherwise. See Recurrence.

English and Swedish only

In 1.0 rules are described in English and Swedish. The scope dialog describes the series' rule in the calendar's locale, so a calendar in another language (Hebrew, German, …) shows that one sentence in English while the rest of the dialog follows your strings.

Right-to-left

components/hebrew-calendar.tsx
'use client'

import type { CalendarEventInput } from '@hapio/calendar-react'

import { HapioCalendar } from '@/components/hapio-calendar/hapio-calendar'

export function HebrewCalendar({ events }: { events: readonly CalendarEventInput[] }) {
  return (
    // The root takes `dir="rtl"` from the locale; pass `dir` to override. Its `lang` is its
    // text's: English (`'en'`) with the built-in strings, `'he'` with Hebrew strings that say so.
    <HapioCalendar events={events} locale="he-IL" timeZone="Asia/Jerusalem" className="h-[36rem]" />
  )
}

In a right-to-left calendar:

  • the layout mirrors: the time axis is on the right, days run from right to left, and the registry's styles use logical properties (ms-, ps-, start-), so nothing needs its own RTL rules;
  • the keys follow the screen: ← and → swap meaning in every composite (the slot cursor, the day cells, the header links, the events, a grabbed event, the view switcher), so → always moves right;
  • the icons of Previous and Next point the way they go.

Text takes its own direction where it can differ from the calendar's. The registry's event time and title each carry dir="auto", so "09:00 – 10:30" keeps its order in a right-to-left calendar and a Latin title too long for its event is cut at its own end ("Haircut · An…", not "…ircut · Anna"); calendar-theme still aligns both by the calendar's direction (text-align: match-parent). The "+N more" text does the same: "+3 more" keeps its order, and in a cell too narrow for it the word is cut, never the count. Give mixed-direction text in your own renderEvent the same dir="auto"; see Customization.