@stacksjs/components 0.2.92 → 0.2.94

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/dist/{Sidebar-yb115a2w.stx → Sidebar-4hn7vw4n.stx} +18 -0
  2. package/dist/index.js +5 -5
  3. package/package.json +5 -6
  4. package/src/components/CodeBlock.stx +0 -54
  5. package/src/components/Footer.stx +0 -66
  6. package/src/components/Hero.stx +0 -25
  7. package/src/components/Installation.stx +0 -37
  8. package/src/components/index.ts +0 -4
  9. package/src/composables/index.ts +0 -4
  10. package/src/composables/useCopyCode.ts +0 -73
  11. package/src/composables/useDarkMode.ts +0 -100
  12. package/src/composables/useSEO.ts +0 -230
  13. package/src/composables/utilities/index.ts +0 -7
  14. package/src/composables/utilities/useClickOutside.ts +0 -72
  15. package/src/composables/utilities/useFocusTrap.ts +0 -159
  16. package/src/composables/utilities/useKeyboard.ts +0 -124
  17. package/src/composables/utilities/useLocalStorage.ts +0 -132
  18. package/src/composables/utilities/useMediaQuery.ts +0 -118
  19. package/src/index.ts +0 -99
  20. package/src/stx.d.ts +0 -4
  21. package/src/ui/accordion/Accordion.stx +0 -146
  22. package/src/ui/accordion/AccordionItem.stx +0 -37
  23. package/src/ui/accordion/index.ts +0 -47
  24. package/src/ui/audio/Audio.stx +0 -50
  25. package/src/ui/audio/README.md +0 -41
  26. package/src/ui/audio/index.ts +0 -13
  27. package/src/ui/auth/Login.stx +0 -161
  28. package/src/ui/auth/README.md +0 -142
  29. package/src/ui/auth/Signup.stx +0 -159
  30. package/src/ui/auth/TwoFactorChallenge.stx +0 -111
  31. package/src/ui/auth/index.ts +0 -32
  32. package/src/ui/avatar/Avatar.stx +0 -77
  33. package/src/ui/avatar/index.ts +0 -12
  34. package/src/ui/badge/Badge.stx +0 -62
  35. package/src/ui/badge/index.ts +0 -10
  36. package/src/ui/breadcrumb/Breadcrumb.stx +0 -79
  37. package/src/ui/breadcrumb/index.ts +0 -15
  38. package/src/ui/button/Button.stx +0 -74
  39. package/src/ui/button/README.md +0 -80
  40. package/src/ui/button/index.ts +0 -85
  41. package/src/ui/calendar/Calendar.stx +0 -189
  42. package/src/ui/calendar/README.md +0 -254
  43. package/src/ui/calendar/index.ts +0 -14
  44. package/src/ui/card/Card.stx +0 -68
  45. package/src/ui/card/index.ts +0 -13
  46. package/src/ui/checkbox/Checkbox.stx +0 -86
  47. package/src/ui/checkbox/index.ts +0 -16
  48. package/src/ui/combobox/Combobox.stx +0 -96
  49. package/src/ui/combobox/ComboboxButton.stx +0 -25
  50. package/src/ui/combobox/ComboboxInput.stx +0 -32
  51. package/src/ui/combobox/ComboboxOption.stx +0 -49
  52. package/src/ui/combobox/ComboboxOptions.stx +0 -16
  53. package/src/ui/combobox/README.md +0 -150
  54. package/src/ui/combobox/index.ts +0 -40
  55. package/src/ui/command-palette/CommandPalette.stx +0 -81
  56. package/src/ui/command-palette/CommandPaletteItem.stx +0 -27
  57. package/src/ui/command-palette/README.md +0 -42
  58. package/src/ui/command-palette/index.ts +0 -15
  59. package/src/ui/dialog/Dialog.stx +0 -41
  60. package/src/ui/dialog/DialogBackdrop.stx +0 -9
  61. package/src/ui/dialog/DialogDescription.stx +0 -11
  62. package/src/ui/dialog/DialogPanel.stx +0 -16
  63. package/src/ui/dialog/DialogTitle.stx +0 -11
  64. package/src/ui/dialog/README.md +0 -121
  65. package/src/ui/dialog/index.ts +0 -32
  66. package/src/ui/drawer/Drawer.stx +0 -120
  67. package/src/ui/drawer/README.md +0 -90
  68. package/src/ui/drawer/index.ts +0 -9
  69. package/src/ui/dropdown/Dropdown.stx +0 -82
  70. package/src/ui/dropdown/DropdownButton.stx +0 -29
  71. package/src/ui/dropdown/DropdownItem.stx +0 -35
  72. package/src/ui/dropdown/DropdownItems.stx +0 -18
  73. package/src/ui/dropdown/README.md +0 -114
  74. package/src/ui/dropdown/index.ts +0 -29
  75. package/src/ui/form/Form.stx +0 -95
  76. package/src/ui/form/index.ts +0 -67
  77. package/src/ui/heatmap/Heatmap.stx +0 -294
  78. package/src/ui/image/Image.stx +0 -165
  79. package/src/ui/image/README.md +0 -164
  80. package/src/ui/image/index.ts +0 -22
  81. package/src/ui/input/EmailInput.stx +0 -30
  82. package/src/ui/input/NumberInput.stx +0 -126
  83. package/src/ui/input/PasswordInput.stx +0 -143
  84. package/src/ui/input/SearchInput.stx +0 -44
  85. package/src/ui/input/TextInput.stx +0 -132
  86. package/src/ui/input/index.ts +0 -180
  87. package/src/ui/listbox/Listbox.stx +0 -101
  88. package/src/ui/listbox/ListboxButton.stx +0 -28
  89. package/src/ui/listbox/ListboxLabel.stx +0 -11
  90. package/src/ui/listbox/ListboxOption.stx +0 -49
  91. package/src/ui/listbox/ListboxOptions.stx +0 -16
  92. package/src/ui/listbox/README.md +0 -135
  93. package/src/ui/listbox/index.ts +0 -39
  94. package/src/ui/navigator/Navigator.stx +0 -113
  95. package/src/ui/navigator/README.md +0 -352
  96. package/src/ui/navigator/index.ts +0 -20
  97. package/src/ui/notification/Notification.stx +0 -117
  98. package/src/ui/notification/README.md +0 -114
  99. package/src/ui/notification/index.ts +0 -12
  100. package/src/ui/pagination/Pagination.stx +0 -150
  101. package/src/ui/pagination/index.ts +0 -11
  102. package/src/ui/payment/Checkout.stx +0 -31
  103. package/src/ui/payment/DefaultPaymentMethod.stx +0 -62
  104. package/src/ui/payment/PaymentMethods.stx +0 -104
  105. package/src/ui/payment/README.md +0 -345
  106. package/src/ui/payment/SubscriptionCheckout.stx +0 -323
  107. package/src/ui/payment/index.ts +0 -59
  108. package/src/ui/popover/Popover.stx +0 -60
  109. package/src/ui/popover/PopoverButton.stx +0 -29
  110. package/src/ui/popover/PopoverPanel.stx +0 -23
  111. package/src/ui/popover/README.md +0 -91
  112. package/src/ui/popover/index.ts +0 -22
  113. package/src/ui/portal/Portal.stx +0 -57
  114. package/src/ui/portal/index.ts +0 -6
  115. package/src/ui/progress/Progress.stx +0 -131
  116. package/src/ui/progress/index.ts +0 -12
  117. package/src/ui/radio/Radio.stx +0 -77
  118. package/src/ui/radio/index.ts +0 -15
  119. package/src/ui/radio-group/README.md +0 -139
  120. package/src/ui/radio-group/RadioGroup.stx +0 -60
  121. package/src/ui/radio-group/RadioGroupDescription.stx +0 -11
  122. package/src/ui/radio-group/RadioGroupLabel.stx +0 -11
  123. package/src/ui/radio-group/RadioGroupOption.stx +0 -58
  124. package/src/ui/radio-group/index.ts +0 -30
  125. package/src/ui/select/README.md +0 -180
  126. package/src/ui/select/Select.stx +0 -82
  127. package/src/ui/select/index.ts +0 -21
  128. package/src/ui/sidebar/Sidebar.stx +0 -458
  129. package/src/ui/sidebar/SidebarFooter.stx +0 -71
  130. package/src/ui/sidebar/SidebarHeader.stx +0 -103
  131. package/src/ui/sidebar/SidebarItem.stx +0 -86
  132. package/src/ui/sidebar/SidebarSection.stx +0 -83
  133. package/src/ui/sidebar/index.ts +0 -153
  134. package/src/ui/sidebar/themes.ts +0 -240
  135. package/src/ui/skeleton/Skeleton.stx +0 -48
  136. package/src/ui/skeleton/index.ts +0 -11
  137. package/src/ui/spinner/Spinner.stx +0 -109
  138. package/src/ui/spinner/index.ts +0 -9
  139. package/src/ui/stepper/README.md +0 -36
  140. package/src/ui/stepper/Stepper.stx +0 -17
  141. package/src/ui/stepper/StepperStep.stx +0 -57
  142. package/src/ui/stepper/index.ts +0 -19
  143. package/src/ui/storage/README.md +0 -193
  144. package/src/ui/storage/index.ts +0 -347
  145. package/src/ui/switch/README.md +0 -114
  146. package/src/ui/switch/Switch.stx +0 -72
  147. package/src/ui/switch/index.ts +0 -10
  148. package/src/ui/table/README.md +0 -39
  149. package/src/ui/table/Table.stx +0 -14
  150. package/src/ui/table/TableBody.stx +0 -10
  151. package/src/ui/table/TableCell.stx +0 -10
  152. package/src/ui/table/TableHead.stx +0 -10
  153. package/src/ui/table/TableHeader.stx +0 -20
  154. package/src/ui/table/TableRow.stx +0 -11
  155. package/src/ui/table/index.ts +0 -35
  156. package/src/ui/tabs/TabPanel.stx +0 -25
  157. package/src/ui/tabs/Tabs.stx +0 -202
  158. package/src/ui/tabs/index.ts +0 -50
  159. package/src/ui/teleport/Teleport.stx +0 -65
  160. package/src/ui/teleport/index.ts +0 -7
  161. package/src/ui/textarea/Textarea.stx +0 -108
  162. package/src/ui/textarea/index.ts +0 -20
  163. package/src/ui/tooltip/Tooltip.stx +0 -71
  164. package/src/ui/tooltip/index.ts +0 -10
  165. package/src/ui/transition/README.md +0 -30
  166. package/src/ui/transition/Transition.stx +0 -102
  167. package/src/ui/transition/index.ts +0 -25
  168. package/src/ui/video/README.md +0 -47
  169. package/src/ui/video/Video.stx +0 -45
  170. package/src/ui/video/index.ts +0 -16
  171. package/src/ui/virtual-list/VirtualList.stx +0 -67
  172. package/src/ui/virtual-list/index.ts +0 -10
  173. package/src/ui/virtual-table/VirtualTable.stx +0 -103
  174. package/src/ui/virtual-table/index.ts +0 -21
  175. package/src/utils/PROP_VALIDATION.md +0 -458
  176. package/src/utils/THEME_SYSTEM.md +0 -702
  177. package/src/utils/accessibility.ts +0 -841
  178. package/src/utils/animation/easing.ts +0 -368
  179. package/src/utils/animation/helpers.ts +0 -223
  180. package/src/utils/animation/index.ts +0 -5
  181. package/src/utils/animation/keyframes.ts +0 -230
  182. package/src/utils/error-handling.ts +0 -475
  183. package/src/utils/highlighter.ts +0 -93
  184. package/src/utils/i18n.ts +0 -634
  185. package/src/utils/keep-alive.ts +0 -819
  186. package/src/utils/lazy-loading.ts +0 -530
  187. package/src/utils/prop-validation.ts +0 -478
  188. package/src/utils/theme.ts +0 -626
@@ -1,93 +0,0 @@
1
- import type { Highlighter as TSHighlighter } from 'ts-syntax-highlighter'
2
- import { createHighlighter } from 'ts-syntax-highlighter'
3
-
4
- let highlighterInstance: TSHighlighter | null = null
5
-
6
- export interface HighlighterOptions {
7
- theme?: 'light' | 'dark' | 'auto'
8
- language?: string
9
- lineNumbers?: boolean
10
- wrapLines?: boolean
11
- }
12
-
13
- export interface HighlightResult {
14
- html: string
15
- language: string
16
- theme: string
17
- }
18
-
19
- /**
20
- * Initialize or get the syntax highlighter instance
21
- */
22
- export async function getHighlighter(): Promise<TSHighlighter> {
23
- if (!highlighterInstance) {
24
- highlighterInstance = await createHighlighter({
25
- theme: 'github-light',
26
- })
27
- }
28
- return highlighterInstance
29
- }
30
-
31
- /**
32
- * Highlight code with syntax highlighting
33
- */
34
- export async function highlight(
35
- code: string,
36
- options: HighlighterOptions = {},
37
- ): Promise<HighlightResult> {
38
- const {
39
- theme = 'auto',
40
- language = 'typescript',
41
- lineNumbers: _lineNumbers = false,
42
- wrapLines: _wrapLines = true,
43
- } = options
44
-
45
- const highlighter = await getHighlighter()
46
-
47
- // Auto-detect theme based on system preference
48
- const effectiveTheme = theme === 'auto'
49
- ? (globalThis.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'github-dark' : 'github-light')
50
- : theme === 'dark' ? 'github-dark' : 'github-light'
51
-
52
- const html = await highlighter.highlight(code, language)
53
-
54
- return {
55
- html: typeof html === 'string' ? html : String(html),
56
- language,
57
- theme: effectiveTheme,
58
- }
59
- }
60
-
61
- /**
62
- * Detect language from code content
63
- */
64
- export function detectLanguage(code: string): string {
65
- // Simple heuristics for language detection
66
- if (code.includes('<!DOCTYPE') || code.includes('<html'))
67
- return 'html'
68
- if (code.includes('<?php'))
69
- return 'php'
70
- if (code.includes('import') && code.includes('from'))
71
- return 'typescript'
72
- if (code.includes('function') || code.includes('const'))
73
- return 'javascript'
74
- if (code.includes('{') && code.includes('}'))
75
- return 'json'
76
- if (code.includes('$') && code.includes('|'))
77
- return 'bash'
78
- if (code.includes('#') && code.includes('##'))
79
- return 'markdown'
80
-
81
- return 'typescript' // Default
82
- }
83
-
84
- /**
85
- * Apply headwind utility classes to highlighted code
86
- */
87
- export function applyHeadwindClasses(html: string, additionalClasses?: string): string {
88
- const baseClasses = 'rounded-md overflow-auto text-sm leading-relaxed font-mono'
89
- const classes = additionalClasses ? `${baseClasses} ${additionalClasses}` : baseClasses
90
-
91
- // Wrap the highlighted HTML with headwind classes
92
- return `<div class="${classes}">${html}</div>`
93
- }
package/src/utils/i18n.ts DELETED
@@ -1,634 +0,0 @@
1
- /**
2
- * Internationalization (i18n) system for @stacksjs/components
3
- *
4
- * Provides translation, locale management, pluralization, and date/number formatting
5
- *
6
- * @example
7
- * ```ts
8
- * import { createI18n, t } from '@stacksjs/components'
9
- *
10
- * // Create i18n instance
11
- * const i18n = createI18n({
12
- * locale: 'en',
13
- * fallbackLocale: 'en',
14
- * messages: {
15
- * en: { welcome: 'Welcome' },
16
- * es: { welcome: 'Bienvenido' }
17
- * }
18
- * })
19
- *
20
- * // Use translations
21
- * t('welcome') // 'Welcome' or 'Bienvenido' based on locale
22
- * ```
23
- */
24
-
25
- /**
26
- * Translation messages structure
27
- */
28
- export type MessageValue = string | { [key: string]: MessageValue }
29
- export type Messages = Record<string, MessageValue>
30
-
31
- /**
32
- * Locale messages
33
- */
34
- export type LocaleMessages = Record<string, Messages>
35
-
36
- /**
37
- * Pluralization rules
38
- */
39
- export type PluralRule = (_count: number, _locale: string) => number
40
-
41
- /**
42
- * I18n configuration
43
- */
44
- export interface I18nConfig {
45
- /** Current locale */
46
- locale: string
47
- /** Fallback locale when translation is missing */
48
- fallbackLocale?: string
49
- /** Translation messages for all locales */
50
- messages: LocaleMessages
51
- /** Custom pluralization rules */
52
- pluralRules?: Record<string, PluralRule>
53
- /** Date formatting options */
54
- dateTimeFormats?: Record<string, Intl.DateTimeFormatOptions>
55
- /** Number formatting options */
56
- numberFormats?: Record<string, Intl.NumberFormatOptions>
57
- /** Missing translation handler */
58
- missing?: (locale: string, key: string) => string | void
59
- /** Whether to warn about missing translations */
60
- warnMissing?: boolean
61
- }
62
-
63
- /**
64
- * I18n instance
65
- */
66
- export interface I18n {
67
- /** Current locale */
68
- locale: string
69
- /** Available locales */
70
- availableLocales: string[]
71
- /** Set current locale */
72
- setLocale: (locale: string) => void
73
- /** Get translation */
74
- t: (key: string, params?: Record<string, any>) => string
75
- /** Get pluralized translation */
76
- tc: (key: string, count: number, params?: Record<string, any>) => string
77
- /** Format date */
78
- d: (date: Date | number, format?: string) => string
79
- /** Format number */
80
- n: (value: number, format?: string) => string
81
- /** Check if translation exists */
82
- te: (key: string) => boolean
83
- /** Add locale messages */
84
- addMessages: (locale: string, messages: Messages) => void
85
- /** Remove locale */
86
- removeLocale: (locale: string) => void
87
- }
88
-
89
- /**
90
- * Global i18n instance
91
- */
92
- let globalI18n: I18n | null = null
93
-
94
- /**
95
- * Default pluralization rule (English-like)
96
- */
97
- const defaultPluralRule: PluralRule = (count: number) => {
98
- return count === 1 ? 0 : 1
99
- }
100
-
101
- /**
102
- * Get nested property from object by path
103
- */
104
- function getNestedProperty(obj: any, path: string): any {
105
- return path.split('.').reduce((current, key) => current?.[key], obj)
106
- }
107
-
108
- /**
109
- * Interpolate parameters in translation string
110
- */
111
- function interpolate(template: string, params: Record<string, any> = {}): string {
112
- return template.replace(/\{(\w+)\}/g, (match, key) => {
113
- return params[key] !== undefined ? String(params[key]) : match
114
- })
115
- }
116
-
117
- /**
118
- * Create an i18n instance
119
- *
120
- * @param config - I18n configuration
121
- * @returns I18n instance
122
- *
123
- * @example
124
- * ```ts
125
- * const i18n = createI18n({
126
- * locale: 'en',
127
- * fallbackLocale: 'en',
128
- * messages: {
129
- * en: {
130
- * welcome: 'Welcome, {name}!',
131
- * items: 'No items | One item | {count} items'
132
- * },
133
- * es: {
134
- * welcome: 'Bienvenido, {name}!',
135
- * items: 'Sin elementos | Un elemento | {count} elementos'
136
- * }
137
- * }
138
- * })
139
- * ```
140
- */
141
- export function createI18n(config: I18nConfig): I18n {
142
- let {
143
- locale,
144
- fallbackLocale = 'en',
145
- messages,
146
- pluralRules = {},
147
- dateTimeFormats = {},
148
- numberFormats = {},
149
- missing,
150
- warnMissing = true,
151
- } = config
152
-
153
- /**
154
- * Get translation for key
155
- */
156
- function t(key: string, params: Record<string, any> = {}): string {
157
- // Try current locale
158
- let translation = getNestedProperty(messages[locale], key)
159
-
160
- // Try fallback locale
161
- if (translation === undefined && fallbackLocale) {
162
- translation = getNestedProperty(messages[fallbackLocale], key)
163
- }
164
-
165
- // Handle missing translation
166
- if (translation === undefined) {
167
- if (missing) {
168
- const result = missing(locale, key)
169
- if (result) {
170
- return interpolate(result, params)
171
- }
172
- }
173
-
174
- if (warnMissing && typeof console !== 'undefined') {
175
- console.warn(`[i18n] Missing translation for key "${key}" in locale "${locale}"`)
176
- }
177
-
178
- return key
179
- }
180
-
181
- // Interpolate parameters
182
- if (typeof translation === 'string') {
183
- return interpolate(translation, params)
184
- }
185
-
186
- return String(translation)
187
- }
188
-
189
- /**
190
- * Get pluralized translation
191
- */
192
- function tc(key: string, count: number, params: Record<string, any> = {}): string {
193
- const translation = t(key, { ...params, count })
194
-
195
- // If translation contains plural forms separated by |
196
- if (translation.includes('|')) {
197
- const forms = translation.split('|').map(s => s.trim())
198
- const pluralRule = pluralRules[locale] || defaultPluralRule
199
- const index = pluralRule(count, locale)
200
-
201
- const form = forms[index] || forms[forms.length - 1]
202
- return interpolate(form, { ...params, count })
203
- }
204
-
205
- return translation
206
- }
207
-
208
- /**
209
- * Format date
210
- */
211
- function d(date: Date | number, format = 'default'): string {
212
- const dateObj = typeof date === 'number' ? new Date(date) : date
213
- const formatOptions = dateTimeFormats[format] || dateTimeFormats.default || {}
214
-
215
- return new Intl.DateTimeFormat(locale, formatOptions).format(dateObj)
216
- }
217
-
218
- /**
219
- * Format number
220
- */
221
- function n(value: number, format = 'default'): string {
222
- const formatOptions = numberFormats[format] || numberFormats.default || {}
223
-
224
- return new Intl.NumberFormat(locale, formatOptions).format(value)
225
- }
226
-
227
- /**
228
- * Check if translation exists
229
- */
230
- function te(key: string): boolean {
231
- const translation = getNestedProperty(messages[locale], key)
232
- if (translation !== undefined) {
233
- return true
234
- }
235
-
236
- if (fallbackLocale) {
237
- const fallbackTranslation = getNestedProperty(messages[fallbackLocale], key)
238
- return fallbackTranslation !== undefined
239
- }
240
-
241
- return false
242
- }
243
-
244
- /**
245
- * Set locale
246
- */
247
- function setLocale(newLocale: string): void {
248
- if (!messages[newLocale]) {
249
- console.warn(`[i18n] Locale "${newLocale}" not found. Available locales: ${Object.keys(messages).join(', ')}`)
250
- return
251
- }
252
- locale = newLocale
253
- }
254
-
255
- /**
256
- * Add messages for a locale
257
- */
258
- function addMessages(addLocale: string, newMessages: Messages): void {
259
- if (!messages[addLocale]) {
260
- messages[addLocale] = {}
261
- }
262
-
263
- // Deep merge messages
264
- messages[addLocale] = {
265
- ...messages[addLocale],
266
- ...newMessages,
267
- }
268
- }
269
-
270
- /**
271
- * Remove a locale
272
- */
273
- function removeLocale(removeLocale: string): void {
274
- delete messages[removeLocale]
275
- }
276
-
277
- return {
278
- get locale() {
279
- return locale
280
- },
281
- get availableLocales() {
282
- return Object.keys(messages)
283
- },
284
- setLocale,
285
- t,
286
- tc,
287
- d,
288
- n,
289
- te,
290
- addMessages,
291
- removeLocale,
292
- }
293
- }
294
-
295
- /**
296
- * Set global i18n instance
297
- *
298
- * @param i18n - I18n instance
299
- */
300
- export function setI18n(i18n: I18n): void {
301
- globalI18n = i18n
302
- }
303
-
304
- /**
305
- * Get global i18n instance
306
- *
307
- * @returns Global i18n instance or null
308
- */
309
- export function getI18n(): I18n | null {
310
- return globalI18n
311
- }
312
-
313
- /**
314
- * Global translation function
315
- * Uses global i18n instance
316
- *
317
- * @param key - Translation key
318
- * @param params - Interpolation parameters
319
- * @returns Translated string
320
- */
321
- export function t(key: string, params?: Record<string, any>): string {
322
- if (!globalI18n) {
323
- console.warn('[i18n] No global i18n instance set. Call setI18n() first.')
324
- return key
325
- }
326
- return globalI18n.t(key, params)
327
- }
328
-
329
- /**
330
- * Global pluralization function
331
- *
332
- * @param key - Translation key
333
- * @param count - Count for pluralization
334
- * @param params - Interpolation parameters
335
- * @returns Pluralized translated string
336
- */
337
- export function tc(key: string, count: number, params?: Record<string, any>): string {
338
- if (!globalI18n) {
339
- console.warn('[i18n] No global i18n instance set. Call setI18n() first.')
340
- return key
341
- }
342
- return globalI18n.tc(key, count, params)
343
- }
344
-
345
- /**
346
- * Global date formatting function
347
- *
348
- * @param date - Date to format
349
- * @param format - Format name
350
- * @returns Formatted date string
351
- */
352
- export function d(date: Date | number, format?: string): string {
353
- if (!globalI18n) {
354
- console.warn('[i18n] No global i18n instance set. Call setI18n() first.')
355
- return String(date)
356
- }
357
- return globalI18n.d(date, format)
358
- }
359
-
360
- /**
361
- * Global number formatting function
362
- *
363
- * @param value - Number to format
364
- * @param format - Format name
365
- * @returns Formatted number string
366
- */
367
- export function n(value: number, format?: string): string {
368
- if (!globalI18n) {
369
- console.warn('[i18n] No global i18n instance set. Call setI18n() first.')
370
- return String(value)
371
- }
372
- return globalI18n.n(value, format)
373
- }
374
-
375
- /**
376
- * Predefined pluralization rules for common languages
377
- */
378
- export const pluralRules: Record<string, PluralRule> = {
379
- // English, German, Dutch, Swedish, Danish, Norwegian, Finnish
380
- en: count => count === 1 ? 0 : 1,
381
- de: count => count === 1 ? 0 : 1,
382
- nl: count => count === 1 ? 0 : 1,
383
- sv: count => count === 1 ? 0 : 1,
384
-
385
- // French, Brazilian Portuguese
386
- fr: count => count === 0 || count === 1 ? 0 : 1,
387
- pt_BR: count => count === 0 || count === 1 ? 0 : 1,
388
-
389
- // Russian, Ukrainian, Serbian, Croatian
390
- ru: (count) => {
391
- if (count % 10 === 1 && count % 100 !== 11)
392
- return 0
393
- if (count % 10 >= 2 && count % 10 <= 4 && (count % 100 < 10 || count % 100 >= 20))
394
- return 1
395
- return 2
396
- },
397
-
398
- // Polish
399
- pl: (count) => {
400
- if (count === 1)
401
- return 0
402
- if (count % 10 >= 2 && count % 10 <= 4 && (count % 100 < 10 || count % 100 >= 20))
403
- return 1
404
- return 2
405
- },
406
-
407
- // Czech, Slovak
408
- cs: (count) => {
409
- if (count === 1)
410
- return 0
411
- if (count >= 2 && count <= 4)
412
- return 1
413
- return 2
414
- },
415
-
416
- // Arabic
417
- ar: (count) => {
418
- if (count === 0)
419
- return 0
420
- if (count === 1)
421
- return 1
422
- if (count === 2)
423
- return 2
424
- if (count % 100 >= 3 && count % 100 <= 10)
425
- return 3
426
- if (count % 100 >= 11)
427
- return 4
428
- return 5
429
- },
430
-
431
- // Japanese, Korean, Chinese, Thai, Vietnamese
432
- ja: () => 0,
433
- ko: () => 0,
434
- zh: () => 0,
435
- th: () => 0,
436
- vi: () => 0,
437
- }
438
-
439
- /**
440
- * Common date/time format presets
441
- */
442
- export const dateTimeFormats: Record<string, Intl.DateTimeFormatOptions> = {
443
- short: {
444
- year: 'numeric',
445
- month: 'numeric',
446
- day: 'numeric',
447
- },
448
- long: {
449
- year: 'numeric',
450
- month: 'long',
451
- day: 'numeric',
452
- },
453
- full: {
454
- weekday: 'long',
455
- year: 'numeric',
456
- month: 'long',
457
- day: 'numeric',
458
- },
459
- time: {
460
- hour: 'numeric',
461
- minute: 'numeric',
462
- },
463
- datetime: {
464
- year: 'numeric',
465
- month: 'numeric',
466
- day: 'numeric',
467
- hour: 'numeric',
468
- minute: 'numeric',
469
- },
470
- }
471
-
472
- /**
473
- * Common number format presets
474
- */
475
- export const numberFormats: Record<string, Intl.NumberFormatOptions> = {
476
- decimal: {
477
- style: 'decimal',
478
- minimumFractionDigits: 2,
479
- maximumFractionDigits: 2,
480
- },
481
- currency: {
482
- style: 'currency',
483
- currency: 'USD',
484
- },
485
- percent: {
486
- style: 'percent',
487
- minimumFractionDigits: 0,
488
- maximumFractionDigits: 2,
489
- },
490
- compact: {
491
- notation: 'compact',
492
- },
493
- }
494
-
495
- /**
496
- * RTL (Right-to-Left) language detection
497
- *
498
- * @param locale - Locale code
499
- * @returns Whether locale is RTL
500
- */
501
- export function isRTL(locale: string): boolean {
502
- const rtlLocales = ['ar', 'he', 'fa', 'ur', 'yi']
503
- const lang = locale.split('-')[0].toLowerCase()
504
- return rtlLocales.includes(lang)
505
- }
506
-
507
- /**
508
- * Load locale messages dynamically
509
- *
510
- * @param locale - Locale code
511
- * @param loadFn - Function to load messages
512
- * @returns Promise of messages
513
- *
514
- * @example
515
- * ```ts
516
- * const messages = await loadLocaleMessages('es', async () => {
517
- * const module = await import('./locales/es.json')
518
- * return module.default
519
- * })
520
- * ```
521
- */
522
- export async function loadLocaleMessages(
523
- locale: string,
524
- loadFn: () => Promise<Messages>,
525
- ): Promise<Messages> {
526
- try {
527
- return await loadFn()
528
- }
529
- catch (error) {
530
- console.error(`[i18n] Failed to load messages for locale "${locale}":`, error)
531
- return {}
532
- }
533
- }
534
-
535
- /**
536
- * Component-specific i18n messages
537
- *
538
- * Provides default translations for component library
539
- */
540
- export const componentMessages: LocaleMessages = {
541
- en: {
542
- common: {
543
- loading: 'Loading...',
544
- error: 'Error',
545
- retry: 'Retry',
546
- cancel: 'Cancel',
547
- confirm: 'Confirm',
548
- close: 'Close',
549
- save: 'Save',
550
- delete: 'Delete',
551
- edit: 'Edit',
552
- search: 'Search',
553
- clear: 'Clear',
554
- submit: 'Submit',
555
- reset: 'Reset',
556
- },
557
- form: {
558
- required: 'This field is required',
559
- invalid: 'Invalid value',
560
- tooShort: 'Value is too short (minimum {min} characters)',
561
- tooLong: 'Value is too long (maximum {max} characters)',
562
- invalidEmail: 'Invalid email address',
563
- invalidUrl: 'Invalid URL',
564
- },
565
- pagination: {
566
- previous: 'Previous',
567
- next: 'Next',
568
- page: 'Page {page}',
569
- of: 'of {total}',
570
- },
571
- },
572
- es: {
573
- common: {
574
- loading: 'Cargando...',
575
- error: 'Error',
576
- retry: 'Reintentar',
577
- cancel: 'Cancelar',
578
- confirm: 'Confirmar',
579
- close: 'Cerrar',
580
- save: 'Guardar',
581
- delete: 'Eliminar',
582
- edit: 'Editar',
583
- search: 'Buscar',
584
- clear: 'Limpiar',
585
- submit: 'Enviar',
586
- reset: 'Restablecer',
587
- },
588
- form: {
589
- required: 'Este campo es obligatorio',
590
- invalid: 'Valor inválido',
591
- tooShort: 'El valor es demasiado corto (mínimo {min} caracteres)',
592
- tooLong: 'El valor es demasiado largo (máximo {max} caracteres)',
593
- invalidEmail: 'Dirección de correo electrónico inválida',
594
- invalidUrl: 'URL inválida',
595
- },
596
- pagination: {
597
- previous: 'Anterior',
598
- next: 'Siguiente',
599
- page: 'Página {page}',
600
- of: 'de {total}',
601
- },
602
- },
603
- fr: {
604
- common: {
605
- loading: 'Chargement...',
606
- error: 'Erreur',
607
- retry: 'Réessayer',
608
- cancel: 'Annuler',
609
- confirm: 'Confirmer',
610
- close: 'Fermer',
611
- save: 'Enregistrer',
612
- delete: 'Supprimer',
613
- edit: 'Modifier',
614
- search: 'Rechercher',
615
- clear: 'Effacer',
616
- submit: 'Soumettre',
617
- reset: 'Réinitialiser',
618
- },
619
- form: {
620
- required: 'Ce champ est obligatoire',
621
- invalid: 'Valeur invalide',
622
- tooShort: 'La valeur est trop courte (minimum {min} caractères)',
623
- tooLong: 'La valeur est trop longue (maximum {max} caractères)',
624
- invalidEmail: 'Adresse e-mail invalide',
625
- invalidUrl: 'URL invalide',
626
- },
627
- pagination: {
628
- previous: 'Précédent',
629
- next: 'Suivant',
630
- page: 'Page {page}',
631
- of: 'sur {total}',
632
- },
633
- },
634
- }