@djangocfg/i18n 2.1.111

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.
package/src/types.ts ADDED
@@ -0,0 +1,656 @@
1
+ import type { PathKeys, TranslationFn } from './utils/path-keys'
2
+
3
+ /**
4
+ * All valid translation keys for base I18nTranslations
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import type { I18nKeys } from '@djangocfg/i18n'
9
+ *
10
+ * const key: I18nKeys = 'ui.form.save' // OK
11
+ * const bad: I18nKeys = 'ui.form.typo' // Error
12
+ * ```
13
+ */
14
+ export type I18nKeys = PathKeys<I18nTranslations>
15
+
16
+ /**
17
+ * Type-safe translation function for base translations
18
+ */
19
+ export type I18nTranslationFn = TranslationFn<I18nTranslations>
20
+
21
+ /**
22
+ * Core translation structure for @djangocfg packages
23
+ *
24
+ * This type defines all translatable strings used across the component library.
25
+ * Each package section contains its own namespace to avoid conflicts.
26
+ */
27
+ export interface I18nTranslations {
28
+ /**
29
+ * UI Components (ui-core, ui-nextjs)
30
+ */
31
+ ui: {
32
+ /** Select/Combobox/MultiSelect components */
33
+ select: {
34
+ placeholder: string
35
+ search: string
36
+ noResults: string
37
+ selectAll: string
38
+ clearAll: string
39
+ /** Template: "+{count} more" */
40
+ moreItems: string
41
+ loading: string
42
+ }
43
+
44
+ /** Pagination components */
45
+ pagination: {
46
+ previous: string
47
+ next: string
48
+ first: string
49
+ last: string
50
+ /** Template: "Page {page}" */
51
+ page: string
52
+ /** Template: "of {total}" */
53
+ of: string
54
+ /** Template: "{from}-{to} of {total}" */
55
+ showing: string
56
+ }
57
+
58
+ /** Dialog/Modal components */
59
+ dialog: {
60
+ close: string
61
+ confirm: string
62
+ cancel: string
63
+ }
64
+
65
+ /** Form components and validation */
66
+ form: {
67
+ submit: string
68
+ cancel: string
69
+ save: string
70
+ delete: string
71
+ edit: string
72
+ create: string
73
+ update: string
74
+ reset: string
75
+ clear: string
76
+ loading: string
77
+ saving: string
78
+ deleting: string
79
+ required: string
80
+ optional: string
81
+ /** Template: "{min} characters minimum" */
82
+ minLength: string
83
+ /** Template: "{max} characters maximum" */
84
+ maxLength: string
85
+ invalidEmail: string
86
+ invalidUrl: string
87
+ invalidPhone: string
88
+ passwordMismatch: string
89
+ }
90
+
91
+ /** Date/Time components */
92
+ datetime: {
93
+ today: string
94
+ yesterday: string
95
+ tomorrow: string
96
+ now: string
97
+ /** Template: "{count} seconds ago" */
98
+ secondsAgo: string
99
+ /** Template: "{count} minutes ago" */
100
+ minutesAgo: string
101
+ /** Template: "{count} hours ago" */
102
+ hoursAgo: string
103
+ /** Template: "{count} days ago" */
104
+ daysAgo: string
105
+ pickDate: string
106
+ pickTime: string
107
+ pickDateTime: string
108
+ pickDateRange: string
109
+ }
110
+
111
+ /** File upload components */
112
+ file: {
113
+ upload: string
114
+ uploading: string
115
+ dragDrop: string
116
+ browse: string
117
+ remove: string
118
+ /** Template: "Max size: {size}" */
119
+ maxSize: string
120
+ invalidType: string
121
+ tooLarge: string
122
+ }
123
+
124
+ /** Table components */
125
+ table: {
126
+ noData: string
127
+ loading: string
128
+ /** Template: "Showing {from}-{to} of {total}" */
129
+ showing: string
130
+ rowsPerPage: string
131
+ sortAsc: string
132
+ sortDesc: string
133
+ filter: string
134
+ clearFilters: string
135
+ selectAll: string
136
+ deselectAll: string
137
+ /** Template: "{count} selected" */
138
+ selected: string
139
+ }
140
+
141
+ /** Common actions */
142
+ actions: {
143
+ view: string
144
+ copy: string
145
+ copied: string
146
+ copyToClipboard: string
147
+ download: string
148
+ share: string
149
+ refresh: string
150
+ retry: string
151
+ expand: string
152
+ collapse: string
153
+ showMore: string
154
+ showLess: string
155
+ seeAll: string
156
+ goBack: string
157
+ remove: string
158
+ previousSlide: string
159
+ nextSlide: string
160
+ }
161
+
162
+ /** Phone input */
163
+ phone: {
164
+ searchCountries: string
165
+ noCountries: string
166
+ }
167
+
168
+ /** Navigation (for tabs, mobile sheets, etc.) */
169
+ navigation: {
170
+ /** Title for navigation sheet/modal */
171
+ title: string
172
+ }
173
+
174
+ /** Common states */
175
+ states: {
176
+ loading: string
177
+ empty: string
178
+ error: string
179
+ success: string
180
+ warning: string
181
+ info: string
182
+ pending: string
183
+ active: string
184
+ inactive: string
185
+ enabled: string
186
+ disabled: string
187
+ }
188
+
189
+ /** Error messages */
190
+ errors: {
191
+ generic: string
192
+ network: string
193
+ timeout: string
194
+ notFound: string
195
+ unauthorized: string
196
+ forbidden: string
197
+ serverError: string
198
+ validation: string
199
+ unknown: string
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Layout components (@djangocfg/layouts)
205
+ */
206
+ layouts: {
207
+ /** Sidebar component */
208
+ sidebar: {
209
+ toggle: string
210
+ collapse: string
211
+ expand: string
212
+ }
213
+
214
+ /** Navigation */
215
+ navigation: {
216
+ menu: string
217
+ home: string
218
+ back: string
219
+ forward: string
220
+ toggleMobile: string
221
+ breadcrumb: string
222
+ }
223
+
224
+ /** Profile/User section */
225
+ profile: {
226
+ settings: string
227
+ account: string
228
+ logout: string
229
+ login: string
230
+ signUp: string
231
+ signOut: string
232
+ userMenu: string
233
+ }
234
+
235
+ /** Theme */
236
+ theme: {
237
+ toggle: string
238
+ light: string
239
+ dark: string
240
+ system: string
241
+ }
242
+
243
+ /** Search */
244
+ search: {
245
+ placeholder: string
246
+ noResults: string
247
+ recentSearches: string
248
+ clearHistory: string
249
+ }
250
+
251
+ /** Mobile drawer */
252
+ mobile: {
253
+ closeMenu: string
254
+ }
255
+
256
+ /** Error pages */
257
+ errors: {
258
+ somethingWentWrong: string
259
+ tryRefreshing: string
260
+ refreshPage: string
261
+ goBack: string
262
+ goHome: string
263
+ needHelp: string
264
+ contactSupport: string
265
+ /** Template: "If the problem persists, please contact {email}" */
266
+ persistsContact: string
267
+ /** Error titles */
268
+ error: string
269
+ badRequest: string
270
+ authRequired: string
271
+ accessDenied: string
272
+ pageNotFound: string
273
+ requestTimeout: string
274
+ serverError: string
275
+ badGateway: string
276
+ serviceUnavailable: string
277
+ gatewayTimeout: string
278
+ /** Error descriptions */
279
+ badRequestDesc: string
280
+ authRequiredDesc: string
281
+ accessDeniedDesc: string
282
+ pageNotFoundDesc: string
283
+ requestTimeoutDesc: string
284
+ serverErrorDesc: string
285
+ badGatewayDesc: string
286
+ serviceUnavailableDesc: string
287
+ gatewayTimeoutDesc: string
288
+ unknownErrorDesc: string
289
+ }
290
+
291
+ /** Push notifications */
292
+ push: {
293
+ enableNotifications: string
294
+ stayUpdated: string
295
+ enable: string
296
+ notNow: string
297
+ dismiss: string
298
+ }
299
+
300
+ /** Auth dialog */
301
+ auth: {
302
+ authRequired: string
303
+ pleaseSignIn: string
304
+ goToSignIn: string
305
+ }
306
+
307
+ /** MCP Chat */
308
+ chat: {
309
+ aiAssistant: string
310
+ defaultTitle: string
311
+ newChat: string
312
+ closeChat: string
313
+ switchToFloating: string
314
+ switchToSidebar: string
315
+ dragToResize: string
316
+ howCanIHelp: string
317
+ }
318
+
319
+ /** PWA Install */
320
+ pwa: {
321
+ /** A2HS Hint */
322
+ addToHomeScreen: string
323
+ installApp: string
324
+ tapToLearnHow: string
325
+ clickToSeeGuide: string
326
+ tapToInstall: string
327
+ installing: string
328
+ dismiss: string
329
+ appLogo: string
330
+ /** iOS Guide */
331
+ iosTitle: string
332
+ iosDescription: string
333
+ iosStep1Title: string
334
+ iosStep1Desc: string
335
+ iosStep2Title: string
336
+ iosStep2Desc: string
337
+ iosStep3Title: string
338
+ iosStep3Desc: string
339
+ gotIt: string
340
+ /** Desktop Guide */
341
+ desktopTitle: string
342
+ desktopDescription: string
343
+ desktopDescSafari: string
344
+ desktopTip: string
345
+ desktopFirefoxNote: string
346
+ /** Desktop steps - Chromium */
347
+ chromiumStep1Title: string
348
+ chromiumStep1Desc: string
349
+ chromiumStep2Title: string
350
+ chromiumStep2Desc: string
351
+ chromiumStep3Title: string
352
+ chromiumStep3Desc: string
353
+ /** Desktop steps - Firefox */
354
+ firefoxStep1Title: string
355
+ firefoxStep1Desc: string
356
+ firefoxStep2Title: string
357
+ firefoxStep2Desc: string
358
+ firefoxStep3Title: string
359
+ firefoxStep3Desc: string
360
+ /** Desktop steps - Safari */
361
+ safariStep1Title: string
362
+ safariStep1Desc: string
363
+ safariStep2Title: string
364
+ safariStep2Desc: string
365
+ /** Desktop steps - Unknown */
366
+ unknownStep1Title: string
367
+ unknownStep1Desc: string
368
+ unknownStep2Title: string
369
+ unknownStep2Desc: string
370
+ unknownStep3Title: string
371
+ unknownStep3Desc: string
372
+ close: string
373
+ }
374
+
375
+ /** Profile page */
376
+ profilePage: {
377
+ /** Page title */
378
+ title: string
379
+ /** Sections */
380
+ account: string
381
+ personalInfo: string
382
+ work: string
383
+ security: string
384
+ actions: string
385
+ /** Fields */
386
+ email: string
387
+ firstName: string
388
+ lastName: string
389
+ company: string
390
+ position: string
391
+ phone: string
392
+ /** Values */
393
+ notSet: string
394
+ on: string
395
+ off: string
396
+ /** Actions */
397
+ save: string
398
+ saving: string
399
+ cancel: string
400
+ signOut: string
401
+ deleteAccount: string
402
+ deleting: string
403
+ /** 2FA */
404
+ twoFactorAuth: string
405
+ /** Avatar */
406
+ avatar: string
407
+ changeAvatar: string
408
+ /** Membership */
409
+ memberSince: string
410
+ /** Placeholders */
411
+ enterField: string
412
+ /** Messages */
413
+ profileUpdated: string
414
+ avatarUpdated: string
415
+ failedToUpdate: string
416
+ failedToUploadAvatar: string
417
+ selectImageFile: string
418
+ fileTooLarge: string
419
+ /** Not authenticated */
420
+ notAuthenticated: string
421
+ pleaseLogIn: string
422
+ /** Delete account dialog */
423
+ deleteAccountTitle: string
424
+ deleteAccountDesc: string
425
+ typeToConfirm: string
426
+ confirmationWord: string
427
+ }
428
+ }
429
+
430
+ /**
431
+ * API/Network related messages (@djangocfg/api)
432
+ */
433
+ api: {
434
+ errors: {
435
+ networkError: string
436
+ connectionFailed: string
437
+ requestTimeout: string
438
+ serverUnavailable: string
439
+ invalidResponse: string
440
+ rateLimited: string
441
+ }
442
+ status: {
443
+ connecting: string
444
+ connected: string
445
+ disconnected: string
446
+ reconnecting: string
447
+ }
448
+ /** Logout dialog */
449
+ logout: {
450
+ title: string
451
+ message: string
452
+ confirm: string
453
+ cancel: string
454
+ }
455
+ }
456
+
457
+ /**
458
+ * Centrifugo WebSocket (@djangocfg/centrifugo)
459
+ */
460
+ centrifugo: {
461
+ /** Monitor component */
462
+ monitor: {
463
+ title: string
464
+ description: string
465
+ openMonitor: string
466
+ openDebugPanel: string
467
+ widgetTitle: string
468
+ }
469
+ /** Tabs */
470
+ tabs: {
471
+ connection: string
472
+ messages: string
473
+ subscriptions: string
474
+ }
475
+ /** Subscriptions list */
476
+ subscriptionsList: {
477
+ title: string
478
+ notConnected: string
479
+ noActiveSubscriptions: string
480
+ }
481
+ /** Status */
482
+ status: {
483
+ connected: string
484
+ disconnected: string
485
+ connecting: string
486
+ connect: string
487
+ uptime: string
488
+ subscriptions: string
489
+ realtimeUnavailable: string
490
+ }
491
+ /** Messages feed */
492
+ feed: {
493
+ title: string
494
+ noMessages: string
495
+ pausedClickToResume: string
496
+ autoScrollOn: string
497
+ autoScrollOff: string
498
+ viewData: string
499
+ }
500
+ /** Filters */
501
+ filters: {
502
+ title: string
503
+ active: string
504
+ clear: string
505
+ searchMessages: string
506
+ level: string
507
+ type: string
508
+ source: string
509
+ autoScrollToLatest: string
510
+ }
511
+ /** Debug panel */
512
+ debug: {
513
+ title: string
514
+ description: string
515
+ openDebugPanel: string
516
+ tabConnection: string
517
+ tabLogs: string
518
+ tabSubscriptions: string
519
+ }
520
+ }
521
+
522
+ /**
523
+ * Heavy tools (@djangocfg/ui-tools)
524
+ */
525
+ tools: {
526
+ /** Code viewer/editor */
527
+ code: {
528
+ copyCode: string
529
+ noContent: string
530
+ }
531
+
532
+ /** Diagram/Mermaid */
533
+ diagram: {
534
+ title: string
535
+ clickToView: string
536
+ }
537
+
538
+ /** Image viewer */
539
+ image: {
540
+ noImage: string
541
+ failedToLoad: string
542
+ zoomIn: string
543
+ zoomOut: string
544
+ fitToView: string
545
+ flipHorizontal: string
546
+ flipVertical: string
547
+ rotate: string
548
+ fullscreen: string
549
+ }
550
+
551
+ /** Gallery/Lightbox */
552
+ gallery: {
553
+ noImages: string
554
+ lightbox: string
555
+ previous: string
556
+ next: string
557
+ close: string
558
+ download: string
559
+ share: string
560
+ }
561
+
562
+ /** Video player */
563
+ video: {
564
+ play: string
565
+ fullscreen: string
566
+ unavailable: string
567
+ }
568
+
569
+ /** Audio player */
570
+ audio: {
571
+ restart: string
572
+ back: string
573
+ forward: string
574
+ volume: string
575
+ }
576
+
577
+ /** Color picker */
578
+ color: {
579
+ pick: string
580
+ }
581
+
582
+ /** OpenAPI viewer */
583
+ openapi: {
584
+ response: string
585
+ noResponse: string
586
+ required: string
587
+ optional: string
588
+ searchEndpoints: string
589
+ all: string
590
+ methods: string
591
+ path: string
592
+ description: string
593
+ navigation: string
594
+ previous: string
595
+ next: string
596
+ bearerToken: string
597
+ }
598
+
599
+ /** JSON form */
600
+ form: {
601
+ title: string
602
+ }
603
+ }
604
+ }
605
+
606
+ /**
607
+ * Deeply partial type for translation overrides
608
+ */
609
+ export type PartialTranslations = DeepPartial<I18nTranslations>
610
+
611
+ /**
612
+ * Helper type for deep partial
613
+ */
614
+ export type DeepPartial<T> = T extends object
615
+ ? { [P in keyof T]?: DeepPartial<T[P]> }
616
+ : T
617
+
618
+ /**
619
+ * Supported locale codes
620
+ */
621
+ export type LocaleCode = 'en' | 'ru' | 'ko' | string
622
+
623
+ /**
624
+ * I18n context value
625
+ */
626
+ export interface I18nContextValue {
627
+ /** Current locale code */
628
+ locale: LocaleCode
629
+ /** Current translations */
630
+ translations: I18nTranslations
631
+ /**
632
+ * Get translated string by key path
633
+ * @param key - Dot-notation key path (e.g., "ui.select.placeholder")
634
+ * @param params - Optional interpolation parameters
635
+ */
636
+ t: (key: string, params?: Record<string, string | number>) => string
637
+ /**
638
+ * Change current locale
639
+ */
640
+ setLocale: (locale: LocaleCode) => void
641
+ }
642
+
643
+ /**
644
+ * I18n provider props
645
+ */
646
+ export interface I18nProviderProps {
647
+ children: React.ReactNode
648
+ /** Initial locale */
649
+ locale?: LocaleCode
650
+ /** Translations for the current locale */
651
+ translations?: I18nTranslations | PartialTranslations
652
+ /** All available translations by locale */
653
+ allTranslations?: Record<LocaleCode, I18nTranslations | PartialTranslations>
654
+ /** Callback when locale changes */
655
+ onLocaleChange?: (locale: LocaleCode) => void
656
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Get a nested value from an object using dot notation
3
+ *
4
+ * @example
5
+ * getNestedValue({ ui: { select: { placeholder: 'Select...' } } }, 'ui.select.placeholder')
6
+ * // => 'Select...'
7
+ */
8
+ export function getNestedValue(
9
+ obj: Record<string, unknown>,
10
+ path: string
11
+ ): string | undefined {
12
+ const keys = path.split('.')
13
+ let current: unknown = obj
14
+
15
+ for (const key of keys) {
16
+ if (current === null || current === undefined) {
17
+ return undefined
18
+ }
19
+ if (typeof current !== 'object') {
20
+ return undefined
21
+ }
22
+ current = (current as Record<string, unknown>)[key]
23
+ }
24
+
25
+ if (typeof current === 'string') {
26
+ return current
27
+ }
28
+
29
+ return undefined
30
+ }
@@ -0,0 +1,4 @@
1
+ export { interpolate } from './interpolate'
2
+ export { mergeTranslations, createTranslations } from './merge'
3
+ export { getNestedValue } from './get-value'
4
+ export type { PathKeys, TranslationFn, TypedTranslationFn, TranslationKeys } from './path-keys'
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Interpolate variables in a translation string
3
+ *
4
+ * @example
5
+ * interpolate("Hello, {name}!", { name: "World" })
6
+ * // => "Hello, World!"
7
+ *
8
+ * interpolate("+{count} more", { count: 5 })
9
+ * // => "+5 more"
10
+ */
11
+ export function interpolate(
12
+ template: string,
13
+ params?: Record<string, string | number>
14
+ ): string {
15
+ if (!params) return template
16
+
17
+ return template.replace(/\{(\w+)\}/g, (match, key) => {
18
+ if (key in params) {
19
+ return String(params[key])
20
+ }
21
+ return match
22
+ })
23
+ }