import { createContext, useContext, useEffect, useMemo, useState, } from "react"; import { formatDate, formatList, formatNumber, formatRelativeTime, selectMessage, selectPlural, } from "./formatters.ts"; import { formatMessage, localeDirection, normalizeLocale, resolveMessage, type MessageArguments, type SupportedLocale, type TextDirection, } from "./message-contract.ts"; import type { MessageKey } from "./catalog.ts"; type LocaleContextValue = Readonly<{ locale: SupportedLocale; direction: TextDirection; setLocale(locale: SupportedLocale): void; message( key: Key, ...args: MessageArguments ): string; resolve(key: string): string; date( value: Date | number, options?: Parameters[2], ): string; number(value: number, options?: Parameters[2]): string; relativeTime( value: number, unit: Intl.RelativeTimeFormatUnit, ): string; list(values: readonly string[]): string; plural( value: number, choices: Parameters[2], ): string; select( value: string, choices: Readonly & { other: string }>, ): string; }>; const DEFAULT_LOCALE = "ko-KR" as const; const DEFAULT_CONTEXT: LocaleContextValue = Object.freeze({ locale: DEFAULT_LOCALE, direction: "ltr", setLocale() {}, message: (key, ...args) => formatMessage(DEFAULT_LOCALE, key, ...args), resolve: (key) => resolveMessage(DEFAULT_LOCALE, key), date: (value, options) => formatDate(DEFAULT_LOCALE, value, options), number: (value, options) => formatNumber(DEFAULT_LOCALE, value, options), relativeTime: (value, unit) => formatRelativeTime(DEFAULT_LOCALE, value, unit), list: (values) => formatList(DEFAULT_LOCALE, values), plural: (value, choices) => selectPlural(DEFAULT_LOCALE, value, choices), select: (value, choices) => selectMessage(value, choices), }); const LocaleContext = createContext(DEFAULT_CONTEXT); export function LocaleProvider({ children, initialLocale = "ko-KR", }: Readonly<{ children: React.ReactNode; initialLocale?: SupportedLocale; }>) { const [locale, setLocaleState] = useState(() => normalizeLocale(initialLocale), ); const direction = localeDirection(locale); useEffect(() => { document.documentElement.lang = locale; document.documentElement.dir = direction; }, [direction, locale]); const value = useMemo( () => ({ locale, direction, setLocale: setLocaleState, message: (key, ...args) => formatMessage(locale, key, ...args), resolve: (key) => resolveMessage(locale, key), date: (dateValue, options) => formatDate(locale, dateValue, options), number: (numberValue, options) => formatNumber(locale, numberValue, options), relativeTime: (relativeValue, unit) => formatRelativeTime(locale, relativeValue, unit), list: (values) => formatList(locale, values), plural: (pluralValue, choices) => selectPlural(locale, pluralValue, choices), select: (selectValue, choices) => selectMessage(selectValue, choices), }), [direction, locale], ); return ( {children} ); } export function useLocale(): LocaleContextValue { return useContext(LocaleContext); }