feat(ui): add framework-neutral localization support
- add a lightweight UI i18n provider with translation and locale contexts\n- localize accessibility labels across breadcrumb, carousel, dialog, pagination, sheet, sidebar, spinner, and toast components\n- integrate calendar locale data without coupling UI components to Lingui\n- publish tree-shakeable catalogs for eight common locales
This commit is contained in:
@@ -5,12 +5,16 @@ import {
|
||||
DayPicker,
|
||||
getDefaultClassNames,
|
||||
type DayButton,
|
||||
type Locale,
|
||||
} from "react-day-picker"
|
||||
|
||||
import { cn } from "@workspace/ui/lib/utils"
|
||||
import { Button, buttonVariants } from "@workspace/ui/components/button"
|
||||
import { ChevronLeftIcon, ChevronRightIcon, ChevronDownIcon } from "lucide-react"
|
||||
import { useCalendarLocale, useLocale } from "@workspace/ui/components/i18n"
|
||||
import {
|
||||
ChevronLeftIcon,
|
||||
ChevronRightIcon,
|
||||
ChevronDownIcon,
|
||||
} from "lucide-react"
|
||||
|
||||
function Calendar({
|
||||
className,
|
||||
@@ -18,7 +22,8 @@ function Calendar({
|
||||
showOutsideDays = true,
|
||||
captionLayout = "label",
|
||||
buttonVariant = "ghost",
|
||||
locale,
|
||||
locale: providedLocale,
|
||||
lang,
|
||||
formatters,
|
||||
components,
|
||||
...props
|
||||
@@ -26,6 +31,10 @@ function Calendar({
|
||||
buttonVariant?: React.ComponentProps<typeof Button>["variant"]
|
||||
}) {
|
||||
const defaultClassNames = getDefaultClassNames()
|
||||
const contextLocale = useLocale()
|
||||
const contextCalendarLocale = useCalendarLocale()
|
||||
const locale = providedLocale ?? contextCalendarLocale
|
||||
const localeCode = locale?.code ?? contextLocale
|
||||
|
||||
return (
|
||||
<DayPicker
|
||||
@@ -38,9 +47,10 @@ function Calendar({
|
||||
)}
|
||||
captionLayout={captionLayout}
|
||||
locale={locale}
|
||||
lang={lang ?? localeCode}
|
||||
formatters={{
|
||||
formatMonthDropdown: (date) =>
|
||||
date.toLocaleString(locale?.code, { month: "short" }),
|
||||
date.toLocaleString(localeCode, { month: "short" }),
|
||||
...formatters,
|
||||
}}
|
||||
classNames={{
|
||||
@@ -147,13 +157,19 @@ function Calendar({
|
||||
Chevron: ({ className, orientation, ...props }) => {
|
||||
if (orientation === "left") {
|
||||
return (
|
||||
<ChevronLeftIcon className={cn("rtl:rotate-180 size-4", className)} {...props} />
|
||||
<ChevronLeftIcon
|
||||
className={cn("size-4 rtl:rotate-180", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (orientation === "right") {
|
||||
return (
|
||||
<ChevronRightIcon className={cn("rtl:rotate-180 size-4", className)} {...props} />
|
||||
<ChevronRightIcon
|
||||
className={cn("size-4 rtl:rotate-180", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -162,7 +178,7 @@ function Calendar({
|
||||
)
|
||||
},
|
||||
DayButton: ({ ...props }) => (
|
||||
<CalendarDayButton locale={locale} {...props} />
|
||||
<CalendarDayButton locale={localeCode} {...props} />
|
||||
),
|
||||
WeekNumber: ({ children, ...props }) => {
|
||||
return (
|
||||
@@ -186,7 +202,7 @@ function CalendarDayButton({
|
||||
modifiers,
|
||||
locale,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DayButton> & { locale?: Partial<Locale> }) {
|
||||
}: React.ComponentProps<typeof DayButton> & { locale?: string }) {
|
||||
const defaultClassNames = getDefaultClassNames()
|
||||
|
||||
const ref = React.useRef<HTMLButtonElement>(null)
|
||||
@@ -198,7 +214,7 @@ function CalendarDayButton({
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
data-day={day.date.toLocaleDateString(locale?.code)}
|
||||
data-day={day.date.toLocaleDateString(locale)}
|
||||
data-selected-single={
|
||||
modifiers.selected &&
|
||||
!modifiers.range_start &&
|
||||
|
||||
Reference in New Issue
Block a user