ae1798f6f4
- 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
157 lines
3.6 KiB
TypeScript
157 lines
3.6 KiB
TypeScript
"use client"
|
|
|
|
import * as React from "react"
|
|
import type { Locale as DayPickerLocale } from "react-day-picker"
|
|
|
|
export type MessageValues = Record<string, unknown>
|
|
|
|
export interface MessageDescriptor {
|
|
id: string
|
|
message: string
|
|
comment?: string
|
|
values?: MessageValues
|
|
}
|
|
|
|
export type TranslateFunction = (
|
|
descriptor: MessageDescriptor,
|
|
values?: MessageValues
|
|
) => string
|
|
|
|
export type CalendarLocale = Partial<DayPickerLocale>
|
|
|
|
type UiMessages = Record<string, MessageDescriptor>
|
|
|
|
export const uiMessages = {
|
|
breadcrumb: /* i18n */ {
|
|
id: "ui.breadcrumb.label",
|
|
message: "Breadcrumb",
|
|
},
|
|
close: /* i18n */ {
|
|
id: "ui.common.close",
|
|
message: "Close",
|
|
},
|
|
closeToast: /* i18n */ {
|
|
id: "ui.toast.close",
|
|
message: "Close notification",
|
|
},
|
|
loading: /* i18n */ {
|
|
id: "ui.common.loading",
|
|
message: "Loading",
|
|
},
|
|
more: /* i18n */ {
|
|
id: "ui.common.more",
|
|
message: "More",
|
|
},
|
|
morePages: /* i18n */ {
|
|
id: "ui.pagination.more",
|
|
message: "More pages",
|
|
},
|
|
next: /* i18n */ {
|
|
id: "ui.pagination.next",
|
|
message: "Next",
|
|
},
|
|
nextPage: /* i18n */ {
|
|
id: "ui.pagination.nextPage",
|
|
message: "Go to next page",
|
|
},
|
|
nextSlide: /* i18n */ {
|
|
id: "ui.carousel.nextSlide",
|
|
message: "Next slide",
|
|
},
|
|
pagination: /* i18n */ {
|
|
id: "ui.pagination.label",
|
|
message: "Pagination",
|
|
},
|
|
previous: /* i18n */ {
|
|
id: "ui.pagination.previous",
|
|
message: "Previous",
|
|
},
|
|
previousPage: /* i18n */ {
|
|
id: "ui.pagination.previousPage",
|
|
message: "Go to previous page",
|
|
},
|
|
previousSlide: /* i18n */ {
|
|
id: "ui.carousel.previousSlide",
|
|
message: "Previous slide",
|
|
},
|
|
sidebar: /* i18n */ {
|
|
id: "ui.sidebar.title",
|
|
message: "Sidebar",
|
|
},
|
|
sidebarDescription: /* i18n */ {
|
|
id: "ui.sidebar.description",
|
|
message: "Displays the mobile sidebar.",
|
|
},
|
|
toggleSidebar: /* i18n */ {
|
|
id: "ui.sidebar.toggle",
|
|
message: "Toggle sidebar",
|
|
},
|
|
} satisfies UiMessages
|
|
|
|
const DEFAULT_LOCALE = "en-US"
|
|
const defaultTranslate: TranslateFunction = (descriptor) => descriptor.message
|
|
|
|
const TranslateContext =
|
|
React.createContext<TranslateFunction>(defaultTranslate)
|
|
const LocaleContext = React.createContext(DEFAULT_LOCALE)
|
|
const CalendarLocaleContext = React.createContext<CalendarLocale | undefined>(
|
|
undefined
|
|
)
|
|
|
|
export interface I18nProviderProps {
|
|
calendarLocale?: CalendarLocale
|
|
children: React.ReactNode
|
|
locale?: string
|
|
t?: TranslateFunction
|
|
}
|
|
|
|
export function I18nProvider({
|
|
calendarLocale,
|
|
children,
|
|
locale = DEFAULT_LOCALE,
|
|
t = defaultTranslate,
|
|
}: I18nProviderProps) {
|
|
return (
|
|
<TranslateContext.Provider value={t}>
|
|
<LocaleContext.Provider value={locale}>
|
|
<CalendarLocaleContext.Provider value={calendarLocale}>
|
|
{children}
|
|
</CalendarLocaleContext.Provider>
|
|
</LocaleContext.Provider>
|
|
</TranslateContext.Provider>
|
|
)
|
|
}
|
|
|
|
export function useTranslate(): TranslateFunction {
|
|
return React.useContext(TranslateContext)
|
|
}
|
|
|
|
export function useMessage(
|
|
descriptor: MessageDescriptor,
|
|
values?: MessageValues
|
|
) {
|
|
const translate = useTranslate()
|
|
|
|
return React.useMemo(
|
|
() => translate(descriptor, values),
|
|
[descriptor, translate, values]
|
|
)
|
|
}
|
|
|
|
export interface LocalizedTextProps {
|
|
message: MessageDescriptor
|
|
values?: MessageValues
|
|
}
|
|
|
|
export function LocalizedText({ message, values }: LocalizedTextProps) {
|
|
return useMessage(message, values)
|
|
}
|
|
|
|
export function useLocale(): string {
|
|
return React.useContext(LocaleContext)
|
|
}
|
|
|
|
export function useCalendarLocale(): CalendarLocale | undefined {
|
|
return React.useContext(CalendarLocaleContext)
|
|
}
|