@coherent.js/i18n 1.0.0-rc.6 → 1.0.1

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 (2) hide show
  1. package/package.json +4 -3
  2. package/types/index.d.ts +235 -330
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coherent.js/i18n",
3
- "version": "1.0.0-rc.6",
3
+ "version": "1.0.1",
4
4
  "description": "Internationalization support for Coherent.js applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -23,7 +23,7 @@
23
23
  "author": "Coherent.js Team",
24
24
  "license": "MIT",
25
25
  "peerDependencies": {
26
- "@coherent.js/core": "1.0.0-rc.6"
26
+ "@coherent.js/core": "1.0.1"
27
27
  },
28
28
  "repository": {
29
29
  "type": "git",
@@ -51,6 +51,7 @@
51
51
  "build": "node build.mjs",
52
52
  "clean": "rm -rf dist",
53
53
  "test": "vitest run",
54
- "test:watch": "vitest"
54
+ "test:watch": "vitest",
55
+ "typecheck": "tsc -p tsconfig.typecheck.json --noEmit"
55
56
  }
56
57
  }
package/types/index.d.ts CHANGED
@@ -3,354 +3,236 @@
3
3
  * @module @coherent.js/i18n
4
4
  */
5
5
 
6
- import type { CoherentNode } from '@coherent.js/core';
7
-
8
6
  // ============================================================================
9
- // Translation Key Types
7
+ // Translation Messages
10
8
  // ============================================================================
11
9
 
12
- /**
13
- * Translation key type (can be extended for type-safe translations)
14
- */
10
+ /** Translation key, dot-separated for nested lookups (`'home.title'`). */
15
11
  export type TranslationKey = string;
16
12
 
17
13
  /**
18
- * Nested translation messages object
14
+ * A translation tree. Leaves are strings, or plural objects keyed by CLDR
15
+ * category (`one`, `other`, ...) selected via a `count` parameter.
19
16
  */
20
17
  export type TranslationMessages = {
21
- [key: string]: string | TranslationMessages;
18
+ [key: string]: string | PluralForms | TranslationMessages;
22
19
  };
23
20
 
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;
21
+ /** Plural variants for one key, selected by `Intl.PluralRules`. */
22
+ export interface PluralForms {
23
+ zero?: string;
24
+ one?: string;
25
+ two?: string;
26
+ few?: string;
27
+ many?: string;
28
+ other?: string;
29
+ }
54
30
 
55
- /**
56
- * Translate a key with interpolation and pluralization
57
- */
58
- (key: TranslationKey, params: Record<string, string | number>, count: number): string;
31
+ /** Interpolation parameters; `count` also drives plural selection. */
32
+ export interface TranslationParams {
33
+ count?: number;
34
+ [param: string]: unknown;
59
35
  }
60
36
 
61
37
  // ============================================================================
62
- // I18n Configuration
38
+ // Translator
63
39
  // ============================================================================
64
40
 
65
- /**
66
- * I18n configuration options
67
- */
68
- export interface I18nConfig {
69
- /** Default locale code (e.g., 'en', 'en-US') */
70
- defaultLocale: string;
71
- /** List of supported locale codes */
72
- supportedLocales: string[];
73
- /** Fallback locale when translation is missing */
41
+ export interface TranslatorOptions {
42
+ /** Locale used until `setLocale()` is called; defaults to `'en'` */
43
+ defaultLocale?: string;
44
+ /** Locale consulted when a key is missing; defaults to `'en'` */
74
45
  fallbackLocale?: string;
75
- /** Async function to load messages for a locale */
76
- loadMessages?: (locale: string) => Promise<TranslationMessages>;
77
- /** Pre-loaded messages by locale */
78
- messages?: Record<string, TranslationMessages>;
79
- /** Handler for missing translation keys */
80
- missingKeyHandler?: (key: string, locale: string) => string;
81
- /** Interpolation settings */
46
+ /** Called instead of returning the key when a translation is missing */
47
+ missingKeyHandler?: ((key: string, locale: string) => string) | null;
82
48
  interpolation?: {
83
- /** Interpolation prefix (default: '{{') */
49
+ /** Defaults to `'{{'` */
84
50
  prefix?: string;
85
- /** Interpolation suffix (default: '}}') */
51
+ /** Defaults to `'}}'` */
86
52
  suffix?: string;
87
- /** Escape HTML in interpolated values */
88
- escapeHtml?: boolean;
89
53
  };
90
- /** Enable pluralization support */
91
- pluralization?: boolean;
92
- /** Context separator for contextual translations */
93
- contextSeparator?: string;
54
+ [option: string]: unknown;
94
55
  }
95
56
 
96
- // ============================================================================
97
- // I18n Instance
98
- // ============================================================================
99
-
100
57
  /**
101
- * I18n instance interface
58
+ * Holds translations per locale and resolves keys with interpolation,
59
+ * pluralization and fallback.
60
+ *
61
+ * ```ts
62
+ * const t = new Translator({ defaultLocale: 'fr' });
63
+ * t.addTranslations('fr', { greeting: 'Bonjour {{name}}' });
64
+ * t.t('greeting', { name: 'Ada' }); // 'Bonjour Ada'
65
+ * ```
102
66
  */
103
- export interface I18nInstance {
104
- /** Current locale code */
105
- readonly locale: string;
67
+ export class Translator {
68
+ constructor(options?: TranslatorOptions);
106
69
 
107
- /** Translation function */
108
- t: TranslationFunction;
70
+ options: TranslatorOptions;
71
+ translations: Map<string, TranslationMessages>;
72
+ currentLocale: string;
73
+ loadedLocales: Set<string>;
109
74
 
110
- /**
111
- * Change the current locale
112
- */
113
- setLocale(locale: string): Promise<void>;
75
+ /** Deep-merge messages into a locale */
76
+ addTranslations(locale: string, translations: TranslationMessages): void;
114
77
 
115
- /**
116
- * Get the current locale code
117
- */
78
+ /** Recursively merge `source` into `target` */
79
+ deepMerge(target: TranslationMessages, source: TranslationMessages): TranslationMessages;
80
+
81
+ /** Switch the active locale */
82
+ setLocale(locale: string): void;
83
+
84
+ /** The active locale */
118
85
  getLocale(): string;
119
86
 
120
87
  /**
121
- * Get list of supported locales
88
+ * Resolve a key. Falls back to the fallback locale, then to
89
+ * `missingKeyHandler`, then to the key itself.
122
90
  */
123
- getSupportedLocales(): string[];
91
+ t(key: TranslationKey, params?: TranslationParams, locale?: string | null): string;
124
92
 
125
- /**
126
- * Check if a translation key exists
127
- */
128
- hasTranslation(key: TranslationKey): boolean;
93
+ /** Look a key up in one locale without fallback; `null` if absent */
94
+ getTranslation(
95
+ key: TranslationKey,
96
+ locale: string
97
+ ): string | PluralForms | TranslationMessages | null;
129
98
 
130
- /**
131
- * Format a number according to locale
132
- */
133
- formatNumber(value: number, options?: Intl.NumberFormatOptions): string;
99
+ /** Pick the plural form matching `count` */
100
+ selectPlural(pluralObject: PluralForms, count: number, locale: string): string;
134
101
 
135
- /**
136
- * Format a date according to locale
137
- */
138
- formatDate(value: Date | number | string, options?: Intl.DateTimeFormatOptions): string;
102
+ /** Substitute `{{param}}` placeholders */
103
+ interpolate(str: string, params: TranslationParams): string;
139
104
 
140
- /**
141
- * Format a currency value
142
- */
143
- formatCurrency(
144
- value: number,
145
- currency: string,
146
- options?: Intl.NumberFormatOptions
147
- ): string;
105
+ /** Whether a key resolves in the given (or current) locale */
106
+ has(key: TranslationKey, locale?: string | null): boolean;
148
107
 
149
- /**
150
- * Format a relative time (e.g., "3 days ago")
151
- */
152
- formatRelativeTime(
153
- value: number,
154
- unit: Intl.RelativeTimeFormatUnit,
155
- options?: Intl.RelativeTimeFormatOptions
156
- ): string;
108
+ /** All messages for a locale, or `{}` */
109
+ getTranslations(locale?: string | null): TranslationMessages;
157
110
 
158
- /**
159
- * Format a list (e.g., "A, B, and C")
160
- */
161
- formatList(values: string[], options?: Intl.ListFormatOptions): string;
111
+ /** Locales that have been marked loaded */
112
+ getLoadedLocales(): string[];
162
113
 
163
- /**
164
- * Add messages for a locale
165
- */
166
- addMessages(messages: TranslationMessages, locale?: string): void;
114
+ /** Drop a locale's messages, resetting the current locale if it was active */
115
+ removeLocale(locale: string): void;
167
116
 
168
- /**
169
- * Get all messages for current locale
170
- */
171
- getMessages(): TranslationMessages;
117
+ /** Drop every locale and reset to the default */
118
+ clear(): void;
172
119
  }
173
120
 
121
+ /** Create a {@link Translator}. */
122
+ export function createTranslator(options?: TranslatorOptions): Translator;
123
+
124
+ /**
125
+ * Wrap a translator so every key is prefixed with `namespace`.
126
+ */
127
+ export function createScopedTranslator(
128
+ translator: Translator,
129
+ namespace: string
130
+ ): {
131
+ t(key: TranslationKey, params?: TranslationParams, locale?: string | null): string;
132
+ has(key: TranslationKey, locale?: string | null): boolean;
133
+ getLocale(): string;
134
+ setLocale(locale: string): void;
135
+ };
174
136
 
175
137
  // ============================================================================
176
- // Translator Class
138
+ // Formatters
177
139
  // ============================================================================
178
140
 
179
- /**
180
- * Translator options
181
- */
182
- export interface TranslatorOptions {
183
- /** Current locale code */
141
+ /** Locale-aware date and time formatting. */
142
+ export class DateFormatter {
143
+ constructor(locale?: string);
144
+
184
145
  locale: string;
185
- /** Translation messages for current locale */
186
- messages: TranslationMessages;
187
- /** Fallback locale code */
188
- fallbackLocale?: string;
189
- /** Fallback messages */
190
- fallbackMessages?: TranslationMessages;
191
- /** Interpolation settings */
192
- interpolation?: {
193
- prefix?: string;
194
- suffix?: string;
195
- };
196
- /** Enable pluralization */
197
- pluralization?: boolean;
198
- /** Context separator */
199
- contextSeparator?: string;
200
- }
201
146
 
202
- /**
203
- * Translator class
204
- */
205
- export class Translator {
206
- constructor(options: TranslatorOptions);
147
+ /** Format with explicit `Intl.DateTimeFormat` options */
148
+ format(date: Date | number | string, options?: Intl.DateTimeFormatOptions): string;
207
149
 
208
- /**
209
- * Translate a key
210
- */
211
- t(key: string, params?: Record<string, unknown>): string;
150
+ /** Short date (e.g. `1/15/25`) */
151
+ short(date: Date | number | string): string;
212
152
 
213
- /**
214
- * Alias for t()
215
- */
216
- translate(key: string, params?: Record<string, unknown>): string;
153
+ /** Medium date (e.g. `Jan 15, 2025`) */
154
+ medium(date: Date | number | string): string;
217
155
 
218
- /**
219
- * Check if a translation exists
220
- */
221
- has(key: string): boolean;
156
+ /** Long date (e.g. `January 15, 2025`) */
157
+ long(date: Date | number | string): string;
222
158
 
223
- /**
224
- * Set the current locale
225
- */
226
- setLocale(locale: string): void;
159
+ /** Full date, including weekday */
160
+ full(date: Date | number | string): string;
227
161
 
228
- /**
229
- * Get the current locale
230
- */
231
- getLocale(): string;
162
+ /** Time only */
163
+ time(date: Date | number | string, options?: Intl.DateTimeFormatOptions): string;
232
164
 
233
- /**
234
- * Add translation messages
235
- */
236
- addMessages(messages: TranslationMessages, locale?: string): void;
165
+ /** Date and time together */
166
+ dateTime(date: Date | number | string, options?: Intl.DateTimeFormatOptions): string;
237
167
 
238
- /**
239
- * Remove translation messages
240
- */
241
- removeMessages(keys: string[], locale?: string): void;
168
+ /** Relative to now (e.g. `2 hours ago`) */
169
+ relative(date: Date | number | string): string;
242
170
  }
243
171
 
244
- /**
245
- * Create a translator instance
246
- */
247
- export function createTranslator(options: TranslatorOptions): Translator;
248
-
249
- /**
250
- * Create a scoped translator (prefixes all keys)
251
- */
252
- export function createScopedTranslator(translator: Translator, scope: string): Translator;
172
+ /** Locale-aware number formatting. */
173
+ export class NumberFormatter {
174
+ constructor(locale?: string);
253
175
 
254
- // ============================================================================
255
- // Formatters
256
- // ============================================================================
176
+ locale: string;
257
177
 
258
- /**
259
- * Date formatter options
260
- */
261
- export interface DateFormatterOptions {
262
- locale?: string;
263
- timeZone?: string;
264
- dateStyle?: 'full' | 'long' | 'medium' | 'short';
265
- timeStyle?: 'full' | 'long' | 'medium' | 'short';
266
- format?: string;
267
- }
178
+ /** Format with explicit `Intl.NumberFormat` options */
179
+ format(value: number, options?: Intl.NumberFormatOptions): string;
268
180
 
269
- /**
270
- * Date formatter class
271
- */
272
- export class DateFormatter {
273
- constructor(locale?: string, options?: DateFormatterOptions);
181
+ /** Fixed number of fraction digits; defaults to 2 */
182
+ decimal(value: number, decimals?: number): string;
274
183
 
275
- /** Format a date */
276
- format(date: Date | number | string): string;
184
+ /** Percentage; `value` is a ratio, so `0.42` renders as `42%` */
185
+ percent(value: number, decimals?: number): string;
277
186
 
278
- /** Format as relative time (e.g., "2 hours ago") */
279
- formatRelative(date: Date | number | string): string;
187
+ /** Compact notation (e.g. `1.2K`) */
188
+ compact(value: number): string;
280
189
 
281
- /** Format distance between dates */
282
- formatDistance(date: Date | number | string, baseDate?: Date | number): string;
190
+ /** Value with a unit (e.g. `5 km`) */
191
+ unit(value: number, unit: string, options?: Intl.NumberFormatOptions): string;
283
192
  }
284
193
 
285
- /**
286
- * Number formatter options
287
- */
288
- export interface NumberFormatterOptions {
289
- locale?: string;
290
- style?: 'decimal' | 'currency' | 'percent' | 'unit';
291
- currency?: string;
292
- minimumFractionDigits?: number;
293
- maximumFractionDigits?: number;
294
- useGrouping?: boolean;
295
- }
194
+ /** Locale-aware currency formatting. */
195
+ export class CurrencyFormatter {
196
+ constructor(locale?: string, defaultCurrency?: string);
296
197
 
297
- /**
298
- * Number formatter class
299
- */
300
- export class NumberFormatter {
301
- constructor(locale?: string, options?: NumberFormatterOptions);
198
+ locale: string;
302
199
 
303
- /** Format a number */
304
- format(value: number): string;
200
+ /** Format using the given currency, or the configured default */
201
+ format(value: number, currency?: string | null, options?: Intl.NumberFormatOptions): string;
305
202
 
306
- /** Format as compact notation */
307
- formatCompact(value: number): string;
203
+ /** No fraction digits */
204
+ whole(value: number, currency?: string | null): string;
308
205
 
309
- /** Format as percentage */
310
- formatPercent(value: number): string;
311
- }
206
+ /** Symbol notation (`$1.00`) */
207
+ symbol(value: number, currency?: string | null): string;
312
208
 
313
- /**
314
- * Currency formatter options
315
- */
316
- export interface CurrencyFormatterOptions {
317
- locale?: string;
318
- currency: string;
319
- display?: 'symbol' | 'code' | 'name';
209
+ /** Narrow symbol notation */
210
+ narrowSymbol(value: number, currency?: string | null): string;
211
+
212
+ /** Code notation (`USD 1.00`) */
213
+ code(value: number, currency?: string | null): string;
320
214
  }
321
215
 
322
- /**
323
- * Currency formatter class
324
- */
325
- export class CurrencyFormatter {
326
- constructor(locale?: string, options?: CurrencyFormatterOptions);
216
+ /** Locale-aware list formatting. */
217
+ export class ListFormatter {
218
+ constructor(locale?: string);
327
219
 
328
- /** Format a currency value */
329
- format(value: number): string;
330
- }
220
+ locale: string;
331
221
 
332
- /**
333
- * List formatter options
334
- */
335
- export interface ListFormatterOptions {
336
- locale?: string;
337
- type?: 'conjunction' | 'disjunction' | 'unit';
338
- style?: 'long' | 'short' | 'narrow';
339
- }
222
+ /** Format with explicit `Intl.ListFormat` options */
223
+ format(items: string[], options?: Intl.ListFormatOptions): string;
340
224
 
341
- /**
342
- * List formatter class
343
- */
344
- export class ListFormatter {
345
- constructor(locale?: string, options?: ListFormatterOptions);
225
+ /** Conjunction (`A, B, and C`) */
226
+ and(items: string[]): string;
346
227
 
347
- /** Format a list of items */
348
- format(list: string[]): string;
228
+ /** Disjunction (`A, B, or C`) */
229
+ or(items: string[]): string;
230
+
231
+ /** Unit list (`A, B, C`) */
232
+ unit(items: string[]): string;
349
233
  }
350
234
 
351
- /**
352
- * All formatters for a locale
353
- */
235
+ /** Every formatter for one locale. */
354
236
  export interface Formatters {
355
237
  date: DateFormatter;
356
238
  number: NumberFormatter;
@@ -358,91 +240,114 @@ export interface Formatters {
358
240
  list: ListFormatter;
359
241
  }
360
242
 
361
- /**
362
- * Create all formatters for a locale
363
- */
364
- export function createFormatters(locale: string): Formatters;
243
+ /** Create all four formatters for a locale. */
244
+ export function createFormatters(
245
+ locale?: string,
246
+ options?: { defaultCurrency?: string }
247
+ ): Formatters;
365
248
 
366
249
  // ============================================================================
367
- // Locale Management
250
+ // Locale Utilities
368
251
  // ============================================================================
369
252
 
370
- /**
371
- * Locale configuration
372
- */
373
- export interface LocaleConfig {
374
- /** Locale code (e.g., 'en-US') */
375
- code: string;
376
- /** English name */
377
- name: string;
378
- /** Native name */
379
- nativeName: string;
380
- /** Text direction */
381
- direction?: 'ltr' | 'rtl';
382
- /** Date format pattern */
383
- dateFormat?: string;
384
- /** Time format pattern */
385
- timeFormat?: string;
386
- /** First day of week (0 = Sunday, 1 = Monday, etc.) */
387
- firstDayOfWeek?: number;
253
+ /** Text direction. */
254
+ export type LocaleDirection = 'ltr' | 'rtl';
255
+
256
+ /** A locale code split into its parts. */
257
+ export interface ParsedLocale {
258
+ language: string;
259
+ region: string | null;
260
+ script: string | null;
261
+ full: string;
388
262
  }
389
263
 
264
+ /** The environment's preferred locale, or `'en'` outside a browser. */
265
+ export function detectLocale(): string;
266
+
390
267
  /**
391
- * Locale manager class
268
+ * Lowercase a locale code and normalize the separator. The region is dropped
269
+ * unless `keepRegion` is set, so `'en_US'` gives `'en'`, or `'en-us'` when
270
+ * kept. Empty input gives `'en'`.
392
271
  */
393
- export class LocaleManager {
394
- constructor(locales: LocaleConfig[]);
272
+ export function normalizeLocale(locale: string | null | undefined, keepRegion?: boolean): string;
395
273
 
396
- /** Add a locale configuration */
397
- addLocale(locale: LocaleConfig): void;
274
+ /** Split a locale code into language, region and script. */
275
+ export function parseLocale(locale: string): ParsedLocale;
398
276
 
399
- /** Remove a locale */
400
- removeLocale(code: string): void;
277
+ /** Text direction for a locale. */
278
+ export function getLocaleDirection(locale: string): LocaleDirection;
401
279
 
402
- /** Get locale configuration */
403
- getLocale(code: string): LocaleConfig | undefined;
280
+ /** Whether a locale is right-to-left. */
281
+ export function isRTL(locale: string): boolean;
404
282
 
405
- /** Get all locale configurations */
406
- getAllLocales(): LocaleConfig[];
283
+ /** Human-readable name of a locale, rendered in `displayLocale`. */
284
+ export function getLocaleDisplayName(locale: string, displayLocale?: string): string;
285
+
286
+ /**
287
+ * Pick the closest available locale, falling back to the language subtag and
288
+ * finally to `defaultLocale`.
289
+ */
290
+ export function matchLocale(
291
+ requestedLocale: string,
292
+ availableLocales: string[],
293
+ defaultLocale?: string
294
+ ): string;
407
295
 
408
- /** Set current locale */
409
- setCurrentLocale(code: string): void;
296
+ /** The environment's preferred locales, most preferred first. */
297
+ export function getSupportedLocales(): string[];
410
298
 
411
- /** Get current locale configuration */
412
- getCurrentLocale(): LocaleConfig;
299
+ export interface LocaleManagerOptions {
300
+ /** Locale used when detection and storage yield nothing; defaults to `'en'` */
301
+ defaultLocale?: string;
302
+ /** Locales this app ships; defaults to `['en']` */
303
+ availableLocales?: string[];
304
+ /** localStorage key; defaults to `'coherent-locale'` */
305
+ storageKey?: string;
306
+ /** Detect from the environment on construction; defaults to `true` */
307
+ autoDetect?: boolean;
308
+ [option: string]: unknown;
413
309
  }
414
310
 
415
- /**
416
- * Create a locale manager
417
- */
418
- export function createLocaleManager(locales: LocaleConfig[]): LocaleManager;
311
+ /** Called after the locale changes. */
312
+ export type LocaleChangeListener = (newLocale: string, oldLocale: string) => void;
419
313
 
420
314
  /**
421
- * Detect user's preferred locale
315
+ * Tracks the active locale, persisting it to localStorage where available.
422
316
  */
423
- export function detectLocale(): string;
317
+ export class LocaleManager {
318
+ constructor(options?: LocaleManagerOptions);
424
319
 
425
- /**
426
- * Normalize a locale code (e.g., 'en_US' -> 'en-US')
427
- */
428
- export function getLocaleDirection(locale: string): 'ltr' | 'rtl';
320
+ options: LocaleManagerOptions;
321
+ currentLocale: string;
322
+ listeners: LocaleChangeListener[];
429
323
 
430
- export function getLocaleDisplayName(locale: string, displayLocale?: string): string;
324
+ /** Detect the environment locale and match it against the available ones */
325
+ detectAndMatch(): string;
431
326
 
432
- export function getSupportedLocales(): string[];
327
+ /** The active locale */
328
+ getLocale(): string;
433
329
 
434
- export function matchLocale(requestedLocale: string, availableLocales: string[], defaultLocale?: string): string;
330
+ /** Match, store and broadcast a new locale; a no-op if unchanged */
331
+ setLocale(locale: string): void;
435
332
 
436
- export function parseLocale(locale: string): { language: string; region: string | null; script: string | null; full: string };
333
+ /** Subscribe to locale changes; returns an unsubscribe function */
334
+ onChange(listener: LocaleChangeListener): () => void;
437
335
 
438
- export function normalizeLocale(locale: string): string;
336
+ /** Invoke every listener, swallowing listener errors */
337
+ notifyListeners(oldLocale: string, newLocale: string): void;
439
338
 
440
- /**
441
- * Check if a locale uses RTL text direction
442
- */
443
- export function isRTL(locale: string): boolean;
339
+ /** Persist the active locale, ignoring storage failures */
340
+ saveToStorage(): void;
444
341
 
445
- // ============================================================================
446
- // Component Helpers
447
- // ============================================================================
342
+ /** Restore a persisted locale, ignoring storage failures */
343
+ loadFromStorage(): void;
344
+
345
+ /** Copy of the configured available locales */
346
+ getAvailableLocales(): string[];
347
+
348
+ /** Whether a locale is in the available list */
349
+ isAvailable(locale: string): boolean;
350
+ }
448
351
 
352
+ /** Create a {@link LocaleManager}. */
353
+ export function createLocaleManager(options?: LocaleManagerOptions): LocaleManager;