@coherent.js/i18n 1.0.0 → 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.
- package/package.json +4 -3
- 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.
|
|
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.
|
|
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
|
|
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
|
-
*
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
//
|
|
38
|
+
// Translator
|
|
63
39
|
// ============================================================================
|
|
64
40
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
/**
|
|
76
|
-
|
|
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
|
-
/**
|
|
49
|
+
/** Defaults to `'{{'` */
|
|
84
50
|
prefix?: string;
|
|
85
|
-
/**
|
|
51
|
+
/** Defaults to `'}}'` */
|
|
86
52
|
suffix?: string;
|
|
87
|
-
/** Escape HTML in interpolated values */
|
|
88
|
-
escapeHtml?: boolean;
|
|
89
53
|
};
|
|
90
|
-
|
|
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
|
-
*
|
|
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
|
|
104
|
-
|
|
105
|
-
readonly locale: string;
|
|
67
|
+
export class Translator {
|
|
68
|
+
constructor(options?: TranslatorOptions);
|
|
106
69
|
|
|
107
|
-
|
|
108
|
-
|
|
70
|
+
options: TranslatorOptions;
|
|
71
|
+
translations: Map<string, TranslationMessages>;
|
|
72
|
+
currentLocale: string;
|
|
73
|
+
loadedLocales: Set<string>;
|
|
109
74
|
|
|
110
|
-
/**
|
|
111
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
88
|
+
* Resolve a key. Falls back to the fallback locale, then to
|
|
89
|
+
* `missingKeyHandler`, then to the key itself.
|
|
122
90
|
*/
|
|
123
|
-
|
|
91
|
+
t(key: TranslationKey, params?: TranslationParams, locale?: string | null): string;
|
|
124
92
|
|
|
125
|
-
/**
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
160
|
-
*/
|
|
161
|
-
formatList(values: string[], options?: Intl.ListFormatOptions): string;
|
|
111
|
+
/** Locales that have been marked loaded */
|
|
112
|
+
getLoadedLocales(): string[];
|
|
162
113
|
|
|
163
|
-
/**
|
|
164
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
138
|
+
// Formatters
|
|
177
139
|
// ============================================================================
|
|
178
140
|
|
|
179
|
-
/**
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
225
|
-
*/
|
|
226
|
-
setLocale(locale: string): void;
|
|
159
|
+
/** Full date, including weekday */
|
|
160
|
+
full(date: Date | number | string): string;
|
|
227
161
|
|
|
228
|
-
/**
|
|
229
|
-
|
|
230
|
-
*/
|
|
231
|
-
getLocale(): string;
|
|
162
|
+
/** Time only */
|
|
163
|
+
time(date: Date | number | string, options?: Intl.DateTimeFormatOptions): string;
|
|
232
164
|
|
|
233
|
-
/**
|
|
234
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
/**
|
|
276
|
-
|
|
184
|
+
/** Percentage; `value` is a ratio, so `0.42` renders as `42%` */
|
|
185
|
+
percent(value: number, decimals?: number): string;
|
|
277
186
|
|
|
278
|
-
/**
|
|
279
|
-
|
|
187
|
+
/** Compact notation (e.g. `1.2K`) */
|
|
188
|
+
compact(value: number): string;
|
|
280
189
|
|
|
281
|
-
/**
|
|
282
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
/**
|
|
307
|
-
|
|
203
|
+
/** No fraction digits */
|
|
204
|
+
whole(value: number, currency?: string | null): string;
|
|
308
205
|
|
|
309
|
-
/**
|
|
310
|
-
|
|
311
|
-
}
|
|
206
|
+
/** Symbol notation (`$1.00`) */
|
|
207
|
+
symbol(value: number, currency?: string | null): string;
|
|
312
208
|
|
|
313
|
-
/**
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
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
|
-
|
|
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
|
-
|
|
329
|
-
format(value: number): string;
|
|
330
|
-
}
|
|
220
|
+
locale: string;
|
|
331
221
|
|
|
332
|
-
/**
|
|
333
|
-
|
|
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
|
-
|
|
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
|
-
/**
|
|
348
|
-
|
|
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
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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
|
|
250
|
+
// Locale Utilities
|
|
368
251
|
// ============================================================================
|
|
369
252
|
|
|
370
|
-
/**
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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
|
-
*
|
|
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
|
|
394
|
-
constructor(locales: LocaleConfig[]);
|
|
272
|
+
export function normalizeLocale(locale: string | null | undefined, keepRegion?: boolean): string;
|
|
395
273
|
|
|
396
|
-
|
|
397
|
-
|
|
274
|
+
/** Split a locale code into language, region and script. */
|
|
275
|
+
export function parseLocale(locale: string): ParsedLocale;
|
|
398
276
|
|
|
399
|
-
|
|
400
|
-
|
|
277
|
+
/** Text direction for a locale. */
|
|
278
|
+
export function getLocaleDirection(locale: string): LocaleDirection;
|
|
401
279
|
|
|
402
|
-
|
|
403
|
-
|
|
280
|
+
/** Whether a locale is right-to-left. */
|
|
281
|
+
export function isRTL(locale: string): boolean;
|
|
404
282
|
|
|
405
|
-
|
|
406
|
-
|
|
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
|
-
|
|
409
|
-
|
|
296
|
+
/** The environment's preferred locales, most preferred first. */
|
|
297
|
+
export function getSupportedLocales(): string[];
|
|
410
298
|
|
|
411
|
-
|
|
412
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
315
|
+
* Tracks the active locale, persisting it to localStorage where available.
|
|
422
316
|
*/
|
|
423
|
-
export
|
|
317
|
+
export class LocaleManager {
|
|
318
|
+
constructor(options?: LocaleManagerOptions);
|
|
424
319
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
export function getLocaleDirection(locale: string): 'ltr' | 'rtl';
|
|
320
|
+
options: LocaleManagerOptions;
|
|
321
|
+
currentLocale: string;
|
|
322
|
+
listeners: LocaleChangeListener[];
|
|
429
323
|
|
|
430
|
-
|
|
324
|
+
/** Detect the environment locale and match it against the available ones */
|
|
325
|
+
detectAndMatch(): string;
|
|
431
326
|
|
|
432
|
-
|
|
327
|
+
/** The active locale */
|
|
328
|
+
getLocale(): string;
|
|
433
329
|
|
|
434
|
-
|
|
330
|
+
/** Match, store and broadcast a new locale; a no-op if unchanged */
|
|
331
|
+
setLocale(locale: string): void;
|
|
435
332
|
|
|
436
|
-
|
|
333
|
+
/** Subscribe to locale changes; returns an unsubscribe function */
|
|
334
|
+
onChange(listener: LocaleChangeListener): () => void;
|
|
437
335
|
|
|
438
|
-
|
|
336
|
+
/** Invoke every listener, swallowing listener errors */
|
|
337
|
+
notifyListeners(oldLocale: string, newLocale: string): void;
|
|
439
338
|
|
|
440
|
-
/**
|
|
441
|
-
|
|
442
|
-
*/
|
|
443
|
-
export function isRTL(locale: string): boolean;
|
|
339
|
+
/** Persist the active locale, ignoring storage failures */
|
|
340
|
+
saveToStorage(): void;
|
|
444
341
|
|
|
445
|
-
|
|
446
|
-
|
|
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;
|