@frank-auth/react
Version:
Flexible and customizable React UI components for Frank Authentication
1 lines • 13.3 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","sources":["../../../src/locales/index.ts"],"sourcesContent":["export * from './types';\nexport * from './en';\nexport * from './es';\nexport * from './fr';\nexport * from './de';\nexport * from './pt';\nexport * from './it';\nexport * from './ja';\nexport * from './ko';\nexport * from './zh';\n\nimport type {Locale, LocaleDirection, LocaleMessages} from './types';\nimport {en} from './en';\nimport {es} from './es';\nimport {fr} from './fr';\nimport {de} from './de';\nimport {pt} from './pt';\nimport {it} from './it';\nimport {ja} from './ja';\nimport {ko} from './ko';\nimport {zh} from './zh';\n\nexport type {Locale, LocaleMessages};\n\n// Available locales\nexport const AVAILABLE_LOCALES: Locale[] = [\n 'en',\n 'es',\n 'fr',\n 'de',\n 'pt',\n 'it',\n 'ja',\n 'ko',\n 'zh',\n];\n\n// Locale registry\nexport const LOCALE_REGISTRY: Record<Locale, LocaleMessages> = {\n en,\n es,\n fr,\n de,\n pt,\n it,\n ja,\n ko,\n zh,\n};\n\n// Default locale\nexport const DEFAULT_LOCALE: Locale = 'en';\n\n// Locale information\nexport const LOCALE_INFO: Record<Locale, {\n name: string;\n nativeName: string;\n region: string;\n direction: LocaleDirection;\n dateFormat: string;\n timeFormat: string;\n numberFormat: Intl.NumberFormatOptions;\n pluralRules: Intl.PluralRules;\n}> = {\n en: {\n name: 'English',\n nativeName: 'English',\n region: 'US',\n direction: 'ltr',\n dateFormat: 'MM/dd/yyyy',\n timeFormat: 'h:mm a',\n numberFormat: {\n style: 'decimal',\n currency: 'USD',\n },\n pluralRules: new Intl.PluralRules('en'),\n },\n es: {\n name: 'Spanish',\n nativeName: 'Español',\n region: 'ES',\n direction: 'ltr',\n dateFormat: 'dd/MM/yyyy',\n timeFormat: 'HH:mm',\n numberFormat: {\n style: 'decimal',\n currency: 'EUR',\n },\n pluralRules: new Intl.PluralRules('es'),\n },\n fr: {\n name: 'French',\n nativeName: 'Français',\n region: 'FR',\n direction: 'ltr',\n dateFormat: 'dd/MM/yyyy',\n timeFormat: 'HH:mm',\n numberFormat: {\n style: 'decimal',\n currency: 'EUR',\n },\n pluralRules: new Intl.PluralRules('fr'),\n },\n de: {\n name: 'German',\n nativeName: 'Deutsch',\n region: 'DE',\n direction: 'ltr',\n dateFormat: 'dd.MM.yyyy',\n timeFormat: 'HH:mm',\n numberFormat: {\n style: 'decimal',\n currency: 'EUR',\n },\n pluralRules: new Intl.PluralRules('de'),\n },\n pt: {\n name: 'Portuguese',\n nativeName: 'Português',\n region: 'PT',\n direction: 'ltr',\n dateFormat: 'dd/MM/yyyy',\n timeFormat: 'HH:mm',\n numberFormat: {\n style: 'decimal',\n currency: 'EUR',\n },\n pluralRules: new Intl.PluralRules('pt'),\n },\n it: {\n name: 'Italian',\n nativeName: 'Italiano',\n region: 'IT',\n direction: 'ltr',\n dateFormat: 'dd/MM/yyyy',\n timeFormat: 'HH:mm',\n numberFormat: {\n style: 'decimal',\n currency: 'EUR',\n },\n pluralRules: new Intl.PluralRules('it'),\n },\n ja: {\n name: 'Japanese',\n nativeName: '日本語',\n region: 'JP',\n direction: 'ltr',\n dateFormat: 'yyyy/MM/dd',\n timeFormat: 'HH:mm',\n numberFormat: {\n style: 'decimal',\n currency: 'JPY',\n },\n pluralRules: new Intl.PluralRules('pt'),\n },\n ko: {\n name: 'Korean',\n nativeName: '한국어',\n region: 'KR',\n direction: 'ltr',\n dateFormat: 'yyyy. MM. dd.',\n timeFormat: 'HH:mm',\n numberFormat: {\n style: 'decimal',\n currency: 'KRW',\n },\n pluralRules: new Intl.PluralRules('ko'),\n },\n zh: {\n name: 'Chinese',\n nativeName: '中文',\n region: 'CN',\n direction: 'ltr',\n dateFormat: 'yyyy/MM/dd',\n timeFormat: 'HH:mm',\n numberFormat: {\n style: 'decimal',\n currency: 'CNY',\n },\n pluralRules: new Intl.PluralRules('zh'),\n },\n};\n\n// Utility functions\nexport const getLocale = (locale: Locale): LocaleMessages => {\n return LOCALE_REGISTRY[locale] || LOCALE_REGISTRY[DEFAULT_LOCALE];\n};\n\nexport const getLocaleInfo = (locale: Locale) => {\n return LOCALE_INFO[locale] || LOCALE_INFO[DEFAULT_LOCALE];\n};\n\nexport const isValidLocale = (locale: string): locale is Locale => {\n return AVAILABLE_LOCALES.includes(locale as Locale);\n};\n\nexport const detectBrowserLocale = (): Locale => {\n if (typeof window === 'undefined') return DEFAULT_LOCALE;\n\n const browserLocale = navigator.language.toLowerCase();\n\n // Check for exact match first\n if (isValidLocale(browserLocale)) {\n return browserLocale;\n }\n\n // Check for language code match (e.g., 'en-US' -> 'en')\n const languageCode = browserLocale.split('-')[0];\n if (isValidLocale(languageCode)) {\n return languageCode;\n }\n\n return DEFAULT_LOCALE;\n};\n\nexport const formatMessage = (\n message: string,\n values: Record<string, string | number> = {}\n): string => {\n return message.replace(/\\{(\\w+)\\}/g, (match, key) => {\n return values[key]?.toString() || match;\n });\n};\n\nexport const pluralize = (\n count: number,\n messages: {\n zero?: string;\n one: string;\n other: string;\n },\n locale: Locale = DEFAULT_LOCALE\n): string => {\n if (count === 0 && messages.zero) {\n return messages.zero;\n }\n\n if (count === 1) {\n return messages.one;\n }\n\n return messages.other;\n};\n\n// Translation helper function\nexport const t = (\n locale: Locale,\n key: string,\n values?: Record<string, string | number>\n): string => {\n const messages = getLocale(locale);\n const keys = key.split('.');\n\n let message: any = messages;\n for (const k of keys) {\n message = message?.[k];\n if (message === undefined) break;\n }\n\n if (typeof message !== 'string') {\n return key; // Return key if translation not found\n }\n\n return values ? formatMessage(message, values) : message;\n};\n\n// RTL support\nexport const isRTL = (locale: Locale): boolean => {\n return getLocaleInfo(locale).direction === 'rtl';\n};\n\n// Number formatting\nexport const formatNumber = (\n value: number,\n locale: Locale,\n options?: Intl.NumberFormatOptions\n): string => {\n try {\n const localeInfo = getLocaleInfo(locale);\n const localeString = `${locale}-${localeInfo.region}`;\n return new Intl.NumberFormat(localeString, options).format(value);\n } catch {\n return value.toString();\n }\n};\n\n// Date formatting\nexport const formatDate = (\n date: Date,\n locale: Locale,\n options?: Intl.DateTimeFormatOptions\n): string => {\n try {\n const localeInfo = getLocaleInfo(locale);\n const localeString = `${locale}-${localeInfo.region}`;\n return new Intl.DateTimeFormat(localeString, options).format(date);\n } catch {\n return date.toISOString();\n }\n};\n\n// Relative time formatting\nexport const formatRelativeTime = (\n date: Date,\n locale: Locale\n): string => {\n try {\n const now = new Date();\n const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);\n\n const localeInfo = getLocaleInfo(locale);\n const localeString = `${locale}-${localeInfo.region}`;\n\n if (typeof Intl.RelativeTimeFormat !== 'undefined') {\n const rtf = new Intl.RelativeTimeFormat(localeString, { numeric: 'auto' });\n\n if (diffInSeconds < 60) {\n return rtf.format(-diffInSeconds, 'second');\n } else if (diffInSeconds < 3600) {\n return rtf.format(-Math.floor(diffInSeconds / 60), 'minute');\n } else if (diffInSeconds < 86400) {\n return rtf.format(-Math.floor(diffInSeconds / 3600), 'hour');\n } else {\n return rtf.format(-Math.floor(diffInSeconds / 86400), 'day');\n }\n }\n\n // Fallback for browsers without RelativeTimeFormat\n const messages = getLocale(locale);\n\n if (diffInSeconds < 60) {\n return messages.common.timeAgo.justNow;\n } else if (diffInSeconds < 3600) {\n const minutes = Math.floor(diffInSeconds / 60);\n return formatMessage(messages.common.timeAgo.minutesAgo, { count: minutes });\n } else if (diffInSeconds < 86400) {\n const hours = Math.floor(diffInSeconds / 3600);\n return formatMessage(messages.common.timeAgo.hoursAgo, { count: hours });\n } else {\n const days = Math.floor(diffInSeconds / 86400);\n return formatMessage(messages.common.timeAgo.daysAgo, { count: days });\n }\n } catch {\n return date.toISOString();\n }\n};\n\n// Validation message formatting\nexport const getValidationMessage = (\n locale: Locale,\n rule: string,\n field: string,\n value?: any\n): string => {\n const messages = getLocale(locale);\n const validationMessages = messages.validation;\n\n const fieldName = field.charAt(0).toUpperCase() + field.slice(1);\n\n switch (rule) {\n case 'required':\n return formatMessage(validationMessages.required, { field: fieldName });\n case 'email':\n return formatMessage(validationMessages.email, { field: fieldName });\n case 'minLength':\n return formatMessage(validationMessages.minLength, { field: fieldName, min: value });\n case 'maxLength':\n return formatMessage(validationMessages.maxLength, { field: fieldName, max: value });\n case 'pattern':\n return formatMessage(validationMessages.pattern, { field: fieldName });\n case 'min':\n return formatMessage(validationMessages.min, { field: fieldName, min: value });\n case 'max':\n return formatMessage(validationMessages.max, { field: fieldName, max: value });\n default:\n return formatMessage(validationMessages.invalid, { field: fieldName });\n }\n};\n\n// Locale persistence\nexport const saveLocaleToStorage = (locale: Locale): void => {\n if (typeof window === 'undefined') return;\n\n try {\n localStorage.setItem('frank-auth-locale', locale);\n } catch {\n // Ignore storage errors\n }\n};\n\nexport const loadLocaleFromStorage = (): Locale | null => {\n if (typeof window === 'undefined') return null;\n\n try {\n const saved = localStorage.getItem('frank-auth-locale');\n return saved && isValidLocale(saved) ? saved : null;\n } catch {\n return null;\n }\n};\n\n// Auto-detect locale with fallback\nexport const detectLocale = (): Locale => {\n // Try to load from storage first\n const savedLocale = loadLocaleFromStorage();\n if (savedLocale) return savedLocale;\n\n // Try to detect from browser\n return detectBrowserLocale();\n};"],"names":["AVAILABLE_LOCALES","LOCALE_REGISTRY","en","es","fr","de","pt","it","ja","ko","zh","DEFAULT_LOCALE","LOCALE_INFO","getLocale","locale"],"mappings":"4RAyBaA,EAA8B,CACvC,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,IACJ,EAGaC,EAAkD,CAAA,GAC3DC,EAAA,GAAA,GACAC,EAAA,GAAA,GACAC,EAAA,GAAA,GACAC,EAAA,GAAA,GACAC,EAAA,GAAA,GACAC,EAAA,GAAA,GACAC,EAAA,GAAA,GACAC,EAAA,GACAC,GAAAA,EAAAA,EACJ,EAGaC,EAAyB,KAGzBC,EASR,CACD,GAAI,CACA,KAAM,UACN,WAAY,UACZ,OAAQ,KACR,UAAW,MACX,WAAY,aACZ,WAAY,SACZ,aAAc,CACV,MAAO,UACP,SAAU,KACd,EACA,YAAa,IAAI,KAAK,YAAY,IAAI,CAC1C,EACA,GAAI,CACA,KAAM,UACN,WAAY,UACZ,OAAQ,KACR,UAAW,MACX,WAAY,aACZ,WAAY,QACZ,aAAc,CACV,MAAO,UACP,SAAU,KACd,EACA,YAAa,IAAI,KAAK,YAAY,IAAI,CAC1C,EACA,GAAI,CACA,KAAM,SACN,WAAY,WACZ,OAAQ,KACR,UAAW,MACX,WAAY,aACZ,WAAY,QACZ,aAAc,CACV,MAAO,UACP,SAAU,KACd,EACA,YAAa,IAAI,KAAK,YAAY,IAAI,CAC1C,EACA,GAAI,CACA,KAAM,SACN,WAAY,UACZ,OAAQ,KACR,UAAW,MACX,WAAY,aACZ,WAAY,QACZ,aAAc,CACV,MAAO,UACP,SAAU,KACd,EACA,YAAa,IAAI,KAAK,YAAY,IAAI,CAC1C,EACA,GAAI,CACA,KAAM,aACN,WAAY,YACZ,OAAQ,KACR,UAAW,MACX,WAAY,aACZ,WAAY,QACZ,aAAc,CACV,MAAO,UACP,SAAU,KACd,EACA,YAAa,IAAI,KAAK,YAAY,IAAI,CAC1C,EACA,GAAI,CACA,KAAM,UACN,WAAY,WACZ,OAAQ,KACR,UAAW,MACX,WAAY,aACZ,WAAY,QACZ,aAAc,CACV,MAAO,UACP,SAAU,KACd,EACA,YAAa,IAAI,KAAK,YAAY,IAAI,CAC1C,EACA,GAAI,CACA,KAAM,WACN,WAAY,MACZ,OAAQ,KACR,UAAW,MACX,WAAY,aACZ,WAAY,QACZ,aAAc,CACV,MAAO,UACP,SAAU,KACd,EACA,YAAa,IAAI,KAAK,YAAY,IAAI,CAC1C,EACA,GAAI,CACA,KAAM,SACN,WAAY,MACZ,OAAQ,KACR,UAAW,MACX,WAAY,gBACZ,WAAY,QACZ,aAAc,CACV,MAAO,UACP,SAAU,KACd,EACA,YAAa,IAAI,KAAK,YAAY,IAAI,CAC1C,EACA,GAAI,CACA,KAAM,UACN,WAAY,KACZ,OAAQ,KACR,UAAW,MACX,WAAY,aACZ,WAAY,QACZ,aAAc,CACV,MAAO,UACP,SAAU,KACd,EACA,YAAa,IAAI,KAAK,YAAY,IAAI,CAAA,CAE9C,EAGaC,EAAaC,GACfb,EAAgBa,CAAM,GAAKb,EAAgBU,CAAc"}