@coherent.js/i18n 1.0.0-beta.3 → 1.0.0-beta.6

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/README.md CHANGED
@@ -18,6 +18,22 @@ For a high-level overview and repository-wide instructions, see the root README:
18
18
  pnpm add @coherent.js/i18n
19
19
  ```
20
20
 
21
+
22
+ ## Exports
23
+
24
+ Internationalization utilities
25
+
26
+ ### Modular Imports (Tree-Shakable)
27
+
28
+ - i18n utilities: `@coherent.js/i18n`
29
+
30
+ ### Example Usage
31
+
32
+ ```javascript
33
+ import { createI18n } from '@coherent.js/i18n';
34
+ ```
35
+
36
+ > **Note**: All exports are tree-shakable. Import only what you need for optimal bundle size.
21
37
  ## Quick start
22
38
 
23
39
  JavaScript (ESM):
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coherent.js/i18n",
3
- "version": "1.0.0-beta.3",
3
+ "version": "1.0.0-beta.6",
4
4
  "description": "Internationalization support for Coherent.js applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -20,7 +20,7 @@
20
20
  "author": "Coherent.js Team",
21
21
  "license": "MIT",
22
22
  "peerDependencies": {
23
- "@coherent.js/core": "1.0.0-beta.3"
23
+ "@coherent.js/core": "1.0.0-beta.6"
24
24
  },
25
25
  "repository": {
26
26
  "type": "git",
@@ -35,6 +35,7 @@
35
35
  "README.md",
36
36
  "types/"
37
37
  ],
38
+ "sideEffects": false,
38
39
  "scripts": {
39
40
  "build": "node build.mjs",
40
41
  "clean": "rm -rf dist"
package/types/index.d.ts CHANGED
@@ -3,41 +3,283 @@
3
3
  * @module @coherent.js/i18n
4
4
  */
5
5
 
6
- // ===== Translator Types =====
6
+ import type { CoherentNode } from '@coherent.js/core';
7
7
 
8
- export interface TranslationMessages {
8
+ // ============================================================================
9
+ // Translation Key Types
10
+ // ============================================================================
11
+
12
+ /**
13
+ * Translation key type (can be extended for type-safe translations)
14
+ */
15
+ export type TranslationKey = string;
16
+
17
+ /**
18
+ * Nested translation messages object
19
+ */
20
+ export type TranslationMessages = {
9
21
  [key: string]: string | TranslationMessages;
22
+ };
23
+
24
+ /**
25
+ * Helper type to flatten nested keys with dot notation
26
+ * @example FlattenKeys<{ home: { title: 'Title' } }> = 'home.title'
27
+ */
28
+ export type FlattenKeys<T, Prefix extends string = ''> = T extends string
29
+ ? Prefix
30
+ : {
31
+ [K in keyof T]: FlattenKeys<
32
+ T[K],
33
+ `${Prefix}${Prefix extends '' ? '' : '.'}${K & string}`
34
+ >;
35
+ }[keyof T];
36
+
37
+ // ============================================================================
38
+ // Translation Function Types
39
+ // ============================================================================
40
+
41
+ /**
42
+ * Translation function with overloads for different use cases
43
+ */
44
+ export interface TranslationFunction {
45
+ /**
46
+ * Translate a key
47
+ */
48
+ (key: TranslationKey): string;
49
+
50
+ /**
51
+ * Translate a key with interpolation parameters
52
+ */
53
+ (key: TranslationKey, params: Record<string, string | number>): string;
54
+
55
+ /**
56
+ * Translate a key with interpolation and pluralization
57
+ */
58
+ (key: TranslationKey, params: Record<string, string | number>, count: number): string;
59
+ }
60
+
61
+ /**
62
+ * Create a typed translator for a specific message shape
63
+ * @template T - The translation messages type
64
+ */
65
+ export function createTypedTranslator<T extends TranslationMessages>(
66
+ messages: T
67
+ ): (key: FlattenKeys<T>, params?: Record<string, string | number>) => string;
68
+
69
+ // ============================================================================
70
+ // I18n Configuration
71
+ // ============================================================================
72
+
73
+ /**
74
+ * I18n configuration options
75
+ */
76
+ export interface I18nConfig {
77
+ /** Default locale code (e.g., 'en', 'en-US') */
78
+ defaultLocale: string;
79
+ /** List of supported locale codes */
80
+ supportedLocales: string[];
81
+ /** Fallback locale when translation is missing */
82
+ fallbackLocale?: string;
83
+ /** Async function to load messages for a locale */
84
+ loadMessages?: (locale: string) => Promise<TranslationMessages>;
85
+ /** Pre-loaded messages by locale */
86
+ messages?: Record<string, TranslationMessages>;
87
+ /** Handler for missing translation keys */
88
+ missingKeyHandler?: (key: string, locale: string) => string;
89
+ /** Interpolation settings */
90
+ interpolation?: {
91
+ /** Interpolation prefix (default: '{{') */
92
+ prefix?: string;
93
+ /** Interpolation suffix (default: '}}') */
94
+ suffix?: string;
95
+ /** Escape HTML in interpolated values */
96
+ escapeHtml?: boolean;
97
+ };
98
+ /** Enable pluralization support */
99
+ pluralization?: boolean;
100
+ /** Context separator for contextual translations */
101
+ contextSeparator?: string;
102
+ }
103
+
104
+ // ============================================================================
105
+ // I18n Instance
106
+ // ============================================================================
107
+
108
+ /**
109
+ * I18n instance interface
110
+ */
111
+ export interface I18nInstance {
112
+ /** Current locale code */
113
+ readonly locale: string;
114
+
115
+ /** Translation function */
116
+ t: TranslationFunction;
117
+
118
+ /**
119
+ * Change the current locale
120
+ */
121
+ setLocale(locale: string): Promise<void>;
122
+
123
+ /**
124
+ * Get the current locale code
125
+ */
126
+ getLocale(): string;
127
+
128
+ /**
129
+ * Get list of supported locales
130
+ */
131
+ getSupportedLocales(): string[];
132
+
133
+ /**
134
+ * Check if a translation key exists
135
+ */
136
+ hasTranslation(key: TranslationKey): boolean;
137
+
138
+ /**
139
+ * Format a number according to locale
140
+ */
141
+ formatNumber(value: number, options?: Intl.NumberFormatOptions): string;
142
+
143
+ /**
144
+ * Format a date according to locale
145
+ */
146
+ formatDate(value: Date | number | string, options?: Intl.DateTimeFormatOptions): string;
147
+
148
+ /**
149
+ * Format a currency value
150
+ */
151
+ formatCurrency(
152
+ value: number,
153
+ currency: string,
154
+ options?: Intl.NumberFormatOptions
155
+ ): string;
156
+
157
+ /**
158
+ * Format a relative time (e.g., "3 days ago")
159
+ */
160
+ formatRelativeTime(
161
+ value: number,
162
+ unit: Intl.RelativeTimeFormatUnit,
163
+ options?: Intl.RelativeTimeFormatOptions
164
+ ): string;
165
+
166
+ /**
167
+ * Format a list (e.g., "A, B, and C")
168
+ */
169
+ formatList(values: string[], options?: Intl.ListFormatOptions): string;
170
+
171
+ /**
172
+ * Add messages for a locale
173
+ */
174
+ addMessages(messages: TranslationMessages, locale?: string): void;
175
+
176
+ /**
177
+ * Get all messages for current locale
178
+ */
179
+ getMessages(): TranslationMessages;
10
180
  }
11
181
 
182
+ /**
183
+ * Create an i18n instance
184
+ */
185
+ export function createI18n(config: I18nConfig): I18nInstance;
186
+
187
+ /**
188
+ * Hook to get the translation function (for component usage)
189
+ */
190
+ export function useTranslation(): TranslationFunction;
191
+
192
+ /**
193
+ * Hook to get and set the current locale
194
+ */
195
+ export function useLocale(): [string, (locale: string) => Promise<void>];
196
+
197
+ // ============================================================================
198
+ // Translator Class
199
+ // ============================================================================
200
+
201
+ /**
202
+ * Translator options
203
+ */
12
204
  export interface TranslatorOptions {
205
+ /** Current locale code */
13
206
  locale: string;
207
+ /** Translation messages for current locale */
14
208
  messages: TranslationMessages;
209
+ /** Fallback locale code */
15
210
  fallbackLocale?: string;
211
+ /** Fallback messages */
16
212
  fallbackMessages?: TranslationMessages;
213
+ /** Interpolation settings */
17
214
  interpolation?: {
18
215
  prefix?: string;
19
216
  suffix?: string;
20
217
  };
218
+ /** Enable pluralization */
21
219
  pluralization?: boolean;
220
+ /** Context separator */
22
221
  contextSeparator?: string;
23
222
  }
24
223
 
224
+ /**
225
+ * Translator class
226
+ */
25
227
  export class Translator {
26
228
  constructor(options: TranslatorOptions);
27
- t(key: string, params?: Record<string, any>): string;
28
- translate(key: string, params?: Record<string, any>): string;
229
+
230
+ /**
231
+ * Translate a key
232
+ */
233
+ t(key: string, params?: Record<string, unknown>): string;
234
+
235
+ /**
236
+ * Alias for t()
237
+ */
238
+ translate(key: string, params?: Record<string, unknown>): string;
239
+
240
+ /**
241
+ * Check if a translation exists
242
+ */
29
243
  has(key: string): boolean;
244
+
245
+ /**
246
+ * Set the current locale
247
+ */
30
248
  setLocale(locale: string): void;
249
+
250
+ /**
251
+ * Get the current locale
252
+ */
31
253
  getLocale(): string;
254
+
255
+ /**
256
+ * Add translation messages
257
+ */
32
258
  addMessages(messages: TranslationMessages, locale?: string): void;
259
+
260
+ /**
261
+ * Remove translation messages
262
+ */
33
263
  removeMessages(keys: string[], locale?: string): void;
34
264
  }
35
265
 
266
+ /**
267
+ * Create a translator instance
268
+ */
36
269
  export function createTranslator(options: TranslatorOptions): Translator;
270
+
271
+ /**
272
+ * Create a scoped translator (prefixes all keys)
273
+ */
37
274
  export function createScopedTranslator(translator: Translator, scope: string): Translator;
38
275
 
39
- // ===== Formatters Types =====
276
+ // ============================================================================
277
+ // Formatters
278
+ // ============================================================================
40
279
 
280
+ /**
281
+ * Date formatter options
282
+ */
41
283
  export interface DateFormatterOptions {
42
284
  locale?: string;
43
285
  timeZone?: string;
@@ -46,13 +288,25 @@ export interface DateFormatterOptions {
46
288
  format?: string;
47
289
  }
48
290
 
291
+ /**
292
+ * Date formatter class
293
+ */
49
294
  export class DateFormatter {
50
295
  constructor(locale?: string, options?: DateFormatterOptions);
296
+
297
+ /** Format a date */
51
298
  format(date: Date | number | string): string;
299
+
300
+ /** Format as relative time (e.g., "2 hours ago") */
52
301
  formatRelative(date: Date | number | string): string;
302
+
303
+ /** Format distance between dates */
53
304
  formatDistance(date: Date | number | string, baseDate?: Date | number): string;
54
305
  }
55
306
 
307
+ /**
308
+ * Number formatter options
309
+ */
56
310
  export interface NumberFormatterOptions {
57
311
  locale?: string;
58
312
  style?: 'decimal' | 'currency' | 'percent' | 'unit';
@@ -62,35 +316,63 @@ export interface NumberFormatterOptions {
62
316
  useGrouping?: boolean;
63
317
  }
64
318
 
319
+ /**
320
+ * Number formatter class
321
+ */
65
322
  export class NumberFormatter {
66
323
  constructor(locale?: string, options?: NumberFormatterOptions);
324
+
325
+ /** Format a number */
67
326
  format(value: number): string;
327
+
328
+ /** Format as compact notation */
68
329
  formatCompact(value: number): string;
330
+
331
+ /** Format as percentage */
69
332
  formatPercent(value: number): string;
70
333
  }
71
334
 
335
+ /**
336
+ * Currency formatter options
337
+ */
72
338
  export interface CurrencyFormatterOptions {
73
339
  locale?: string;
74
340
  currency: string;
75
341
  display?: 'symbol' | 'code' | 'name';
76
342
  }
77
343
 
344
+ /**
345
+ * Currency formatter class
346
+ */
78
347
  export class CurrencyFormatter {
79
348
  constructor(locale?: string, options?: CurrencyFormatterOptions);
349
+
350
+ /** Format a currency value */
80
351
  format(value: number): string;
81
352
  }
82
353
 
354
+ /**
355
+ * List formatter options
356
+ */
83
357
  export interface ListFormatterOptions {
84
358
  locale?: string;
85
359
  type?: 'conjunction' | 'disjunction' | 'unit';
86
360
  style?: 'long' | 'short' | 'narrow';
87
361
  }
88
362
 
363
+ /**
364
+ * List formatter class
365
+ */
89
366
  export class ListFormatter {
90
367
  constructor(locale?: string, options?: ListFormatterOptions);
368
+
369
+ /** Format a list of items */
91
370
  format(list: string[]): string;
92
371
  }
93
372
 
373
+ /**
374
+ * All formatters for a locale
375
+ */
94
376
  export interface Formatters {
95
377
  date: DateFormatter;
96
378
  number: NumberFormatter;
@@ -98,31 +380,97 @@ export interface Formatters {
98
380
  list: ListFormatter;
99
381
  }
100
382
 
383
+ /**
384
+ * Create all formatters for a locale
385
+ */
101
386
  export function createFormatters(locale: string): Formatters;
102
387
 
103
- // ===== Locale Manager Types =====
388
+ // ============================================================================
389
+ // Locale Management
390
+ // ============================================================================
104
391
 
392
+ /**
393
+ * Locale configuration
394
+ */
105
395
  export interface LocaleConfig {
396
+ /** Locale code (e.g., 'en-US') */
106
397
  code: string;
398
+ /** English name */
107
399
  name: string;
400
+ /** Native name */
108
401
  nativeName: string;
402
+ /** Text direction */
109
403
  direction?: 'ltr' | 'rtl';
404
+ /** Date format pattern */
110
405
  dateFormat?: string;
406
+ /** Time format pattern */
111
407
  timeFormat?: string;
408
+ /** First day of week (0 = Sunday, 1 = Monday, etc.) */
112
409
  firstDayOfWeek?: number;
113
410
  }
114
411
 
412
+ /**
413
+ * Locale manager class
414
+ */
115
415
  export class LocaleManager {
116
416
  constructor(locales: LocaleConfig[]);
417
+
418
+ /** Add a locale configuration */
117
419
  addLocale(locale: LocaleConfig): void;
420
+
421
+ /** Remove a locale */
118
422
  removeLocale(code: string): void;
423
+
424
+ /** Get locale configuration */
119
425
  getLocale(code: string): LocaleConfig | undefined;
426
+
427
+ /** Get all locale configurations */
120
428
  getAllLocales(): LocaleConfig[];
429
+
430
+ /** Set current locale */
121
431
  setCurrentLocale(code: string): void;
432
+
433
+ /** Get current locale configuration */
122
434
  getCurrentLocale(): LocaleConfig;
123
435
  }
124
436
 
437
+ /**
438
+ * Create a locale manager
439
+ */
125
440
  export function createLocaleManager(locales: LocaleConfig[]): LocaleManager;
441
+
442
+ /**
443
+ * Detect user's preferred locale
444
+ */
126
445
  export function detectLocale(): string;
446
+
447
+ /**
448
+ * Normalize a locale code (e.g., 'en_US' -> 'en-US')
449
+ */
127
450
  export function normalizeLocale(locale: string): string;
451
+
452
+ /**
453
+ * Check if a locale uses RTL text direction
454
+ */
128
455
  export function isRTL(locale: string): boolean;
456
+
457
+ // ============================================================================
458
+ // Component Helpers
459
+ // ============================================================================
460
+
461
+ /**
462
+ * Create a translated component node
463
+ */
464
+ export function translatedNode(
465
+ key: TranslationKey,
466
+ tagName?: string,
467
+ params?: Record<string, string | number>
468
+ ): CoherentNode;
469
+
470
+ /**
471
+ * Create an i18n context provider component
472
+ */
473
+ export function createI18nProvider(
474
+ i18n: I18nInstance,
475
+ children: CoherentNode
476
+ ): CoherentNode;