@juit/vue-i18n 0.1.3 → 0.2.0

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
@@ -61,16 +61,87 @@ described below, or some options:
61
61
  available in this language.
62
62
  * `translations`: an object containing the translations for the messages to
63
63
  translate, keyed by its identifier.
64
- * `formats`: configurations for number and date formatting:
65
- * `dateTimeFormat`: a string (`short`, `medium`, `long`, or `full`) used to
66
- format date-and-time values, or the options to be
67
- provided to the `Intl.DateTimeFormat` constructor.
68
- * `dateOnlyFormat`: a string (`short`, `medium`, `long`, or `full`) used to
69
- format dates, or the options to be provided to the
70
- `Intl.DateTimeFormat` constructor.
71
- * `timeOnlyFormat`: a string (`short`, `medium`, `long`, or `full`) used to
72
- format times, or the options to be provided to the
73
- `Intl.DateTimeFormat` constructor.
64
+ * `dateTimeFormats`: date and time format aliases used formatting dates.
65
+ * `numberFormats`: number format aliases used formatting numbers.
66
+
67
+
68
+ ### Date Time Format Aliases
69
+
70
+ Date time formatting aliases can be configured keyed by a simple string and
71
+ values as [`Intl.DateTimeFormatOptions`][5]
72
+
73
+ ```typescript
74
+ import { createApp } from 'vue'
75
+ import { i18n } from '@juit/vue-i18n'
76
+ import MyApp from './app.vue'
77
+
78
+ const app = createApp(MyApp).use(i18n, {
79
+ defaultLanguage: 'en',
80
+ dateTimeFormats: {
81
+ custom: {
82
+ year: 'numeric',
83
+ month: '2-digit',
84
+ day: '2-digit',
85
+ },
86
+ }
87
+ })
88
+ ```
89
+
90
+ The default (overridable) formats are as follows:
91
+
92
+ ```typescript
93
+ {
94
+ // used when no alias or date time format is specified
95
+ default: { dateStyle: 'medium', timeStyle: 'medium' },
96
+
97
+ // generic formats
98
+ short: { dateStyle: 'short', timeStyle: 'short' },
99
+ medium: { dateStyle: 'medium', timeStyle: 'medium' },
100
+ long: { dateStyle: 'long', timeStyle: 'long' },
101
+ full: { dateStyle: 'full', timeStyle: 'full' },
102
+
103
+ // date only formats
104
+ date: { dateStyle: 'medium' },
105
+ shortDate: { dateStyle: 'short' },
106
+ mediumDate: { dateStyle: 'medium' },
107
+ longDate: { dateStyle: 'long' },
108
+ fullDate: { dateStyle: 'full' },
109
+
110
+ // time only formats
111
+ time: { timeStyle: 'medium' },
112
+ shortTime: { timeStyle: 'short' },
113
+ mediumTime: { timeStyle: 'medium' },
114
+ longTime: { timeStyle: 'long' },
115
+ fullTime: { timeStyle: 'full' },
116
+ }
117
+ ```
118
+
119
+
120
+ ### Number Format Aliases
121
+
122
+ Similarly to date time, also number formatting aliases can be configured keyed
123
+ by a simple string and values as [`Intl.NumberFormatOptions`][4]
124
+
125
+ ```typescript
126
+ import { createApp } from 'vue'
127
+ import { i18n } from '@juit/vue-i18n'
128
+ import MyApp from './app.vue'
129
+
130
+ const app = createApp(MyApp).use(i18n, {
131
+ defaultLanguage: 'en',
132
+ numberFormats: {
133
+ speed: {
134
+ style: 'unit',
135
+ unit: 'kilometer-per-hour',
136
+ }
137
+ }
138
+ })
139
+ ```
140
+
141
+ While there is no intrinsic default, each valid ISO-4217 currency code
142
+ (e.g. `EUR`, `USD`, ...) can be used as an alias.
143
+
144
+ To configure the default number format use the `default` key.
74
145
 
75
146
 
76
147
  ## Usage
@@ -211,8 +282,10 @@ The default format can be specified when configuring the plugin as the
211
282
  ## Formatting dates
212
283
 
213
284
  The `d(...)` function can be used to format date-and-time values in the current
214
- locale, together with the `d.date(...)` to format only dates, or `d.time(...)`
215
- to format only times.
285
+ locale.
286
+
287
+ When the second parameter is a string, it is considered to be one of the
288
+ _aliases_ configured when the plugin is setup.
216
289
 
217
290
  ```typescript
218
291
  import { useTranslator } from '@juit/vue-i18n'
@@ -220,22 +293,13 @@ import { useTranslator } from '@juit/vue-i18n'
220
293
  const translator = useTranslator()
221
294
 
222
295
  const dateTime = translator.d(new Date()) // e.g. '03.02.2025, 18:08:05' in de-DE
223
- const dateOnly = translator.d.date(new Date()) // e.g. '03.02.2025' in de-DE
224
- const dateTime = translator.d.time(new Date()) // e.g. '18:08:05' in de-DE
296
+ const dateOnly = translator.d(new Date(), 'date') // e.g. '03.02.2025' in de-DE
297
+ const dateTime = translator.d(new Date(), 'time') // e.g. '18:08:05' in de-DE
225
298
  ```
226
299
 
227
300
  A full [`Intl.DateTimeFormatOptions`][5] set of options can also be specified
228
301
  as a second parameter to fine-tune the formatting.
229
302
 
230
- The second parameter can also be a simple string `full`, `long`, `medium`, or
231
- `short`, as a shortcut to `options.dateStyle` or `options.timeStyle`.
232
-
233
- The default formatting options for each method can be specified at configuration
234
- time, using the `formats.dateTimeFormat`, `formats.dateOnlyFormat`, or
235
- `formats.timeOnlyFormat` parameters.
236
-
237
- By default, they are all set to `medium`.
238
-
239
303
 
240
304
  ## Configuring Types
241
305
 
@@ -255,27 +319,59 @@ in the configuration:
255
319
  These are the arbitrary keys used to identify the
256
320
  messages to be translated with the `t` and `tc`
257
321
  methods of `Translator`.
322
+ * `dateTimeFormats`: the date and time formats _aliases_ used by the
323
+ application.
324
+ * `numberFormats`: the number formats _aliases_ used by the application.
258
325
 
259
326
  To configure the types, follow the example below:
260
327
 
261
328
  ```typescript
262
329
  const translations = {
263
- 'hello': { en: 'Hello, world!', de: 'Hallo, Welt!' }
264
- } as const satisfies TranslationsOptions
330
+ hello: { en: 'Hello, world!', de: 'Hallo, Welt!' }
331
+ } as const satisfies Translations
332
+
333
+ const dateTimeFormats = {
334
+ // override the default format
335
+ default: { dateStyle: 'short', timeStyle: 'short' },
336
+ // add a new custom format
337
+ custom: {
338
+ day: '2-digit',
339
+ month: '2-digit',
340
+ year: 'numeric',
341
+ weekday: 'short',
342
+ timeZone: 'UTC',
343
+ },
344
+ } as const satisfies DateTimeFormats
345
+
346
+ const numberFormats = {
347
+ speed: { style: 'unit', unit: 'kilometer-per-hour' },
348
+ } as const satisfies NumberFormats
265
349
 
266
350
  declare module '@juit/vue-i18n' {
267
351
  export interface I18nConfiguration {
268
- translationKeys: keyof typeof translations
269
- languages: 'de' | 'en'
352
+ languages: 'de' | 'en',
353
+ translationKeys: keyof typeof translations,
354
+ dateTimeFormats: keyof typeof dateTimeFormats,
355
+ numberFormats: keyof typeof numberFormats,
270
356
  }
271
357
  }
358
+
359
+ const app = createApp(MyApp).use(i18n, {
360
+ defaultLanguage: 'en',
361
+ translations,
362
+ dateTimeFormats,
363
+ numberFormats,
364
+ })
272
365
  ```
273
366
 
274
367
  In the example above, if any of the translation objects in our app is missing
275
368
  a language (either `en` or `de`), TypeScript will complain.
276
369
 
277
- In the same way, if we pass any other string but `hello` to `t(...)` or `tc(...)`,
278
- TypeScript will report the wrong key.
370
+ In the same way, if we pass any other string but `hello` to `t(...)` or
371
+ `tc(...)`, TypeScript will report the wrong key.
372
+
373
+ Also, date time format aliases will be augumented using the customizations
374
+ specified in `dateTimeFormats` and `numberFormats`.
279
375
 
280
376
 
281
377
  ## Legal Stuff
package/dist/index.d.ts CHANGED
@@ -19,19 +19,48 @@ declare type BaseTranslation = ISOLanguage extends Language ? {
19
19
  export declare type DateInput = Date | string | number | null | undefined;
20
20
 
21
21
  /**
22
- * Options to initialize the date and time formats used by the translation
23
- * system.
22
+ * All known date and time formats aliases.
23
+ *
24
+ * When the `I18nConfig` interface is properly merged with and its contains
25
+ * the `dateTimeFormats` property, this type will represent the list of
26
+ * date and time formats available to the `d(...)` method.
27
+ *
28
+ * When left unconfigured, this type will be `string`.
29
+ */
30
+ export declare type DateTimeFormatAlias = ExtractConfig<I18nConfiguration, string, 'dateTimeFormats'> | 'default' | 'short' | 'medium' | 'long' | 'full' | 'date' | 'shortDate' | 'mediumDate' | 'longDate' | 'fullDate' | 'time' | 'shortTime' | 'mediumTime' | 'longTime' | 'fullTime';
31
+
32
+ /**
33
+ * Options to initialize the date and time format _aliases_ used by the
34
+ * translation system.
35
+ *
36
+ * The default aliases (each can be overridden) are:
24
37
  *
25
- * * `dateTimeFormat`: the default format for times and dates used by `$d(...)`.
26
- * * `dateOnlyFormat`: the default format for dates used by `$d.date(...)`.
27
- * * `timeOnlyFormat`: the default format for times used by `$d.time(...)`.
28
- * * `numberFormat`: the default format for numbers used by `$n(...)`.
38
+ * ```ts
39
+ * {
40
+ * default: { dateStyle: 'medium', timeStyle: 'medium' },
41
+ * short: { dateStyle: 'short', timeStyle: 'short' },
42
+ * medium: { dateStyle: 'medium', timeStyle: 'medium' },
43
+ * long: { dateStyle: 'long', timeStyle: 'long' },
44
+ * full: { dateStyle: 'full', timeStyle: 'full' },
45
+ *
46
+ * // date only formats
47
+ * date: { dateStyle: 'medium' },
48
+ * shortDate: { dateStyle: 'short' },
49
+ * mediumDate: { dateStyle: 'medium' },
50
+ * longDate: { dateStyle: 'long' },
51
+ * fullDate: { dateStyle: 'full' },
52
+ *
53
+ * // time only formats
54
+ * time: { timeStyle: 'medium' },
55
+ * shortTime: { timeStyle: 'short' },
56
+ * mediumTime: { timeStyle: 'medium' },
57
+ * longTime: { timeStyle: 'long' },
58
+ * fullTime: { timeStyle: 'full' },
59
+ * }
60
+ * ```
29
61
  */
30
62
  export declare interface DateTimeFormats {
31
- dateOnlyFormat?: Intl.DateTimeFormatOptions | Intl.DateTimeFormatOptions['dateStyle'];
32
- timeOnlyFormat?: Intl.DateTimeFormatOptions | Intl.DateTimeFormatOptions['timeStyle'];
33
- dateTimeFormat?: Intl.DateTimeFormatOptions | (Intl.DateTimeFormatOptions['dateStyle'] & Intl.DateTimeFormatOptions['timeStyle']);
34
- numberFormat?: Intl.NumberFormatOptions;
63
+ readonly [key: string]: Intl.DateTimeFormatOptions;
35
64
  }
36
65
 
37
66
  /** The language or locale to use at construction */
@@ -66,18 +95,40 @@ export declare function i18n(app: App, optionsOrLanguage: Language | I18nOptions
66
95
  * Those are the arbitrary keys used to identify the
67
96
  * messages to be translated with the `t` and `tc`
68
97
  * methods of `Translator`.
98
+ * * `dateTimeFormats`: the date and time formats _aliases_ used by the
99
+ * application.
100
+ * * `numberFormats`: the number formats _aliases_ used by the application.
69
101
  *
70
102
  * To configure the types, follow the example below:
71
103
  *
72
104
  * ```ts
73
105
  * const translations = {
74
106
  * 'hello': { en: 'Hello, world!', de: 'Hallo, Welt!' }
75
- * } as const satisfies TranslationsOptions
107
+ * } as const satisfies Translations
108
+ *
109
+ * const dateTimeFormats = {
110
+ * // override the default format
111
+ * default: { dateStyle: 'short', timeStyle: 'short' },
112
+ * // add a new custom format
113
+ * custom: {
114
+ * day: '2-digit',
115
+ * month: '2-digit',
116
+ * year: 'numeric',
117
+ * weekday: 'short',
118
+ * timeZone: 'UTC',
119
+ * },
120
+ * } as const satisfies DateTimeFormats
121
+ *
122
+ * const numberFormats = {
123
+ * speed: { style: 'unit', unit: 'kilometer-per-hour' },
124
+ * } as const satisfies NumberFormats
76
125
  *
77
126
  * declare module '@juit/vue-i18n' {
78
127
  * export interface I18nConfiguration {
79
- * translationKeys: keyof typeof translations
80
- * languages: 'de' | 'en'
128
+ * languages: 'de' | 'en',
129
+ * translationKeys: keyof typeof translations,
130
+ * dateTimeFormats: keyof typeof dateTimeFormats,
131
+ * numberFormats: keyof typeof numberFormats,
81
132
  * }
82
133
  * }
83
134
  * ```
@@ -89,11 +140,12 @@ export declare interface I18nConfiguration {
89
140
  export declare interface I18nOptions {
90
141
  defaultLanguage: DefaultLanguage;
91
142
  translations?: Translations;
92
- formats?: DateTimeFormats;
143
+ dateTimeFormats?: DateTimeFormats;
144
+ numberFormats?: NumberFormats;
93
145
  }
94
146
 
95
- /** Array of all known ISO-3166-1 countries */
96
- export declare type IsoCountries = {
147
+ /** All known ISO-3166-1 countries and their names */
148
+ export declare type ISOCountries = {
97
149
  AD: 'Andorra';
98
150
  AE: 'United Arab Emirates';
99
151
  AF: 'Afghanistan';
@@ -344,14 +396,180 @@ export declare type IsoCountries = {
344
396
  ZW: 'Zimbabwe';
345
397
  };
346
398
 
347
- /** Array of all known ISO-3166-1 countries */
348
- export declare type ISOCountry = keyof IsoCountries;
399
+ /** Array of all known ISO-3166-1 country names */
400
+ export declare type ISOCountry = keyof ISOCountries;
401
+
402
+ /** All known ISO-4217 currencies and their name */
403
+ declare type ISOCurrencies = {
404
+ AED: 'United Arab Emirates Dirham';
405
+ AFN: 'Afghan Afghani';
406
+ ALL: 'Albanian Lek';
407
+ AMD: 'Armenian Dram';
408
+ ANG: 'Netherlands Antillean Guilder';
409
+ AOA: 'Angolan Kwanza';
410
+ ARS: 'Argentine Peso';
411
+ AUD: 'Australian Dollar';
412
+ AWG: 'Aruban Florin';
413
+ AZN: 'Azerbaijani Manat';
414
+ BAM: 'Bosnia-Herzegovina Convertible Mark';
415
+ BBD: 'Barbadian Dollar';
416
+ BDT: 'Bangladeshi Taka';
417
+ BGN: 'Bulgarian Lev';
418
+ BHD: 'Bahraini Dinar';
419
+ BIF: 'Burundian Franc';
420
+ BMD: 'Bermudan Dollar';
421
+ BND: 'Brunei Dollar';
422
+ BOB: 'Bolivian Boliviano';
423
+ BRL: 'Brazilian Real';
424
+ BSD: 'Bahamian Dollar';
425
+ BTN: 'Bhutanese Ngultrum';
426
+ BWP: 'Botswanan Pula';
427
+ BYN: 'Belarusian Ruble';
428
+ BZD: 'Belize Dollar';
429
+ CAD: 'Canadian Dollar';
430
+ CDF: 'Congolese Franc';
431
+ CHF: 'Swiss Franc';
432
+ CLP: 'Chilean Peso';
433
+ CNY: 'Chinese Yuan';
434
+ COP: 'Colombian Peso';
435
+ CRC: 'Costa Rican Colón';
436
+ CUC: 'Cuban Convertible Peso';
437
+ CUP: 'Cuban Peso';
438
+ CVE: 'Cape Verdean Escudo';
439
+ CZK: 'Czech Koruna';
440
+ DJF: 'Djiboutian Franc';
441
+ DKK: 'Danish Krone';
442
+ DOP: 'Dominican Peso';
443
+ DZD: 'Algerian Dinar';
444
+ EGP: 'Egyptian Pound';
445
+ ERN: 'Eritrean Nakfa';
446
+ ETB: 'Ethiopian Birr';
447
+ EUR: 'Euro';
448
+ FJD: 'Fijian Dollar';
449
+ FKP: 'Falkland Islands Pound';
450
+ GBP: 'British Pound';
451
+ GEL: 'Georgian Lari';
452
+ GHS: 'Ghanaian Cedi';
453
+ GIP: 'Gibraltar Pound';
454
+ GMD: 'Gambian Dalasi';
455
+ GNF: 'Guinean Franc';
456
+ GTQ: 'Guatemalan Quetzal';
457
+ GYD: 'Guyanaese Dollar';
458
+ HKD: 'Hong Kong Dollar';
459
+ HNL: 'Honduran Lempira';
460
+ HRK: 'Croatian Kuna';
461
+ HTG: 'Haitian Gourde';
462
+ HUF: 'Hungarian Forint';
463
+ IDR: 'Indonesian Rupiah';
464
+ ILS: 'Israeli New Shekel';
465
+ INR: 'Indian Rupee';
466
+ IQD: 'Iraqi Dinar';
467
+ IRR: 'Iranian Rial';
468
+ ISK: 'Icelandic Króna';
469
+ JMD: 'Jamaican Dollar';
470
+ JOD: 'Jordanian Dinar';
471
+ JPY: 'Japanese Yen';
472
+ KES: 'Kenyan Shilling';
473
+ KGS: 'Kyrgystani Som';
474
+ KHR: 'Cambodian Riel';
475
+ KMF: 'Comorian Franc';
476
+ KPW: 'North Korean Won';
477
+ KRW: 'South Korean Won';
478
+ KWD: 'Kuwaiti Dinar';
479
+ KYD: 'Cayman Islands Dollar';
480
+ KZT: 'Kazakhstani Tenge';
481
+ LAK: 'Laotian Kip';
482
+ LBP: 'Lebanese Pound';
483
+ LKR: 'Sri Lankan Rupee';
484
+ LRD: 'Liberian Dollar';
485
+ LSL: 'Lesotho Loti';
486
+ LYD: 'Libyan Dinar';
487
+ MAD: 'Moroccan Dirham';
488
+ MDL: 'Moldovan Leu';
489
+ MGA: 'Malagasy Ariary';
490
+ MKD: 'Macedonian Denar';
491
+ MMK: 'Myanmar Kyat';
492
+ MNT: 'Mongolian Tugrik';
493
+ MOP: 'Macanese Pataca';
494
+ MRU: 'Mauritanian Ouguiya';
495
+ MUR: 'Mauritian Rupee';
496
+ MVR: 'Maldivian Rufiyaa';
497
+ MWK: 'Malawian Kwacha';
498
+ MXN: 'Mexican Peso';
499
+ MYR: 'Malaysian Ringgit';
500
+ MZN: 'Mozambican Metical';
501
+ NAD: 'Namibian Dollar';
502
+ NGN: 'Nigerian Naira';
503
+ NIO: 'Nicaraguan Córdoba';
504
+ NOK: 'Norwegian Krone';
505
+ NPR: 'Nepalese Rupee';
506
+ NZD: 'New Zealand Dollar';
507
+ OMR: 'Omani Rial';
508
+ PAB: 'Panamanian Balboa';
509
+ PEN: 'Peruvian Sol';
510
+ PGK: 'Papua New Guinean Kina';
511
+ PHP: 'Philippine Peso';
512
+ PKR: 'Pakistani Rupee';
513
+ PLN: 'Polish Zloty';
514
+ PYG: 'Paraguayan Guarani';
515
+ QAR: 'Qatari Riyal';
516
+ RON: 'Romanian Leu';
517
+ RSD: 'Serbian Dinar';
518
+ RUB: 'Russian Ruble';
519
+ RWF: 'Rwandan Franc';
520
+ SAR: 'Saudi Riyal';
521
+ SBD: 'Solomon Islands Dollar';
522
+ SCR: 'Seychellois Rupee';
523
+ SDG: 'Sudanese Pound';
524
+ SEK: 'Swedish Krona';
525
+ SGD: 'Singapore Dollar';
526
+ SHP: 'St. Helena Pound';
527
+ SLL: 'Sierra Leonean Leone (1964—2022)';
528
+ SOS: 'Somali Shilling';
529
+ SRD: 'Surinamese Dollar';
530
+ SSP: 'South Sudanese Pound';
531
+ STN: 'São Tomé & Príncipe Dobra';
532
+ SVC: 'Salvadoran Colón';
533
+ SYP: 'Syrian Pound';
534
+ SZL: 'Swazi Lilangeni';
535
+ THB: 'Thai Baht';
536
+ TJS: 'Tajikistani Somoni';
537
+ TMT: 'Turkmenistani Manat';
538
+ TND: 'Tunisian Dinar';
539
+ TOP: 'Tongan Paʻanga';
540
+ TRY: 'Turkish Lira';
541
+ TTD: 'Trinidad & Tobago Dollar';
542
+ TWD: 'New Taiwan Dollar';
543
+ TZS: 'Tanzanian Shilling';
544
+ UAH: 'Ukrainian Hryvnia';
545
+ UGX: 'Ugandan Shilling';
546
+ USD: 'US Dollar';
547
+ UYU: 'Uruguayan Peso';
548
+ UZS: 'Uzbekistani Som';
549
+ VES: 'Venezuelan Bolívar';
550
+ VND: 'Vietnamese Dong';
551
+ VUV: 'Vanuatu Vatu';
552
+ WST: 'Samoan Tala';
553
+ XAF: 'Central African CFA Franc';
554
+ XCD: 'East Caribbean Dollar';
555
+ XDR: 'Special Drawing Rights';
556
+ XOF: 'West African CFA Franc';
557
+ XPF: 'CFP Franc';
558
+ XSU: 'Sucre';
559
+ YER: 'Yemeni Rial';
560
+ ZAR: 'South African Rand';
561
+ ZMW: 'Zambian Kwacha';
562
+ ZWL: 'Zimbabwean Dollar (2009)';
563
+ };
564
+
565
+ /** All known ISO-4217 currency codes */
566
+ declare type ISOCurrency = keyof ISOCurrencies;
349
567
 
350
- /** All known ISO-639-1 languages */
351
- export declare type ISOLanguage = keyof IsoLanguages;
568
+ /** All known ISO-639-1 language codes */
569
+ export declare type ISOLanguage = keyof ISOLanguages;
352
570
 
353
- /** Array of all known ISO-639-1 languages */
354
- export declare type IsoLanguages = {
571
+ /** All known ISO-639-1 languages and their name */
572
+ export declare type ISOLanguages = {
355
573
  aa: 'Afar';
356
574
  ab: 'Abkhazian';
357
575
  ae: 'Avestan';
@@ -544,6 +762,36 @@ export declare type Language = ExtractConfig<I18nConfiguration, ISOLanguage, 'la
544
762
  /** Create a _reactive_ translator object from the given options */
545
763
  export declare function makeTranslator(options: I18nOptions): Translator;
546
764
 
765
+ /**
766
+ * All known number formats aliases.
767
+ *
768
+ * When the `I18nConfig` interface is properly merged with and its contains
769
+ * the `numberFormats` property, this type will represent the list of
770
+ * date and time formats available to the `n(...)` method.
771
+ *
772
+ * When left unconfigured, this type will be `string`.
773
+ */
774
+ export declare type NumberFormatAlias = ExtractConfig<I18nConfiguration, string, 'numberFormats'> | 'default' | ISOCurrency;
775
+
776
+ /**
777
+ * Options to initialize the number format _aliases_ used by the translation
778
+ * system.
779
+ *
780
+ * The default aliases (each can be overridden) are:
781
+ *
782
+ * ```ts
783
+ * {
784
+ * default: { }, // use the default number format
785
+ * EUR: { style: 'currency', currency: 'EUR' },
786
+ * USD: { style: 'currency', currency: 'USD' },
787
+ * // ... all currency codes can be used as aliases
788
+ * }
789
+ * ```
790
+ */
791
+ export declare interface NumberFormats {
792
+ readonly [key: string]: Intl.NumberFormatOptions;
793
+ }
794
+
547
795
  /** Prettify our `Translations` exported type */
548
796
  declare type PrettifyTranslation<T> = {
549
797
  [l in keyof T]: T[l];
@@ -638,45 +886,21 @@ export declare interface Translator {
638
886
  */
639
887
  tc(key: TranslationKey | Translation, n: number, params?: TranslationParams): string;
640
888
  /**
641
- * Format a number into a string according to the current language.
889
+ * Format a number according to the current language.
642
890
  *
643
- * If the `currency` parameter is set, the number will be formatted as a
644
- * currency value, using the specified currency symbol.
891
+ * When `format` is provided, it will be used to configure the number format.
892
+ * This can be one of the aliases specified at initialization, or a
893
+ * fully-fledged `Intl.NumberFormatOptions` object.
645
894
  */
646
- n(value?: number | bigint | null | undefined, currency?: string): string;
895
+ n(value?: number | bigint | null | undefined, format?: NumberFormatAlias | Intl.NumberFormatOptions): string;
647
896
  /**
648
- * Format a number into a string according to the current language.
897
+ * Format date and time according to the current language.
649
898
  *
650
- * When `format` is provided, it will be used to configure the number format,
651
- * otherwise the default `format.numberFormat` plugin option will be used.
899
+ * When `format` is provided, it will be used to configure the date and time
900
+ * format. This can be one of the aliases specified at initialization, or a
901
+ * fully-fledged `Intl.DateTimeFormatOptions` object.
652
902
  */
653
- n(value?: number | bigint | null | undefined, format?: Intl.NumberFormatOptions): string;
654
- d: {
655
- /**
656
- * Format date and time according to the current language.
657
- *
658
- * When `style` is provided, it will be used to configure the date and time
659
- * format, otherwise the default `format.dateTimeFormat` plugin option will
660
- * be used.
661
- */
662
- (date?: DateInput, style?: DateTimeFormats['dateTimeFormat']): string;
663
- /**
664
- * Format the date part (without time) according to the current language.
665
- *
666
- * When `style` is provided, it will be used to configure the date and time
667
- * format, otherwise the default `format.dateOnlyFormat` plugin option will
668
- * be used.
669
- */
670
- date(date?: DateInput, style?: DateTimeFormats['dateOnlyFormat']): string;
671
- /**
672
- * Format the time part (without date) according to the current language.
673
- *
674
- * When `style` is provided, it will be used to configure the date and time
675
- * format, otherwise the default `format.timeOnlyFormat` plugin option will
676
- * be used.
677
- */
678
- time(date?: DateInput, style?: DateTimeFormats['timeOnlyFormat']): string;
679
- };
903
+ d(date?: DateInput, format?: DateTimeFormatAlias | Intl.DateTimeFormatOptions): string;
680
904
  }
681
905
 
682
906
  /** Retrieve the translator instance from the Vue app */
package/dist/index.js CHANGED
@@ -1,126 +1,151 @@
1
- import { shallowRef as T, computed as z, reactive as F, warn as h, inject as I } from "vue";
2
- function L(e) {
3
- const n = typeof e.defaultLanguage == "string" ? new Intl.Locale(e.defaultLanguage) : e.defaultLanguage, a = n.region ? `${n.language}-${n.region}` : n.language, o = e.translations ? structuredClone(e.translations) : {}, {
4
- dateOnlyFormat: i = { dateStyle: "medium" },
5
- timeOnlyFormat: g = { timeStyle: "medium" },
6
- dateTimeFormat: u = { dateStyle: "medium", timeStyle: "medium" },
7
- numberFormat: d = {}
8
- } = e.formats || {}, l = T(new Intl.Locale(a)), m = z(() => {
9
- const { language: t, region: r } = l.value, c = [t];
10
- return r && c.unshift(`${t}-${r}`), t !== a && c.push(a), c;
11
- });
12
- function f(t, r = u) {
13
- if (t == null || t === "") return "";
14
- const c = t instanceof Date ? t : new Date(t), y = typeof r == "string" ? { dateStyle: r, timeStyle: r } : r;
15
- return new Intl.DateTimeFormat(l.value, y).format(c);
1
+ import { shallowRef as h, watch as w, computed as b, reactive as $, warn as c, inject as T } from "vue";
2
+ function v(e) {
3
+ let n;
4
+ try {
5
+ n = new Intl.DisplayNames("en-US", { type: "language" }).of(e.language);
6
+ } catch {
16
7
  }
17
- function p(t, r = i) {
18
- return f(t, typeof r == "string" ? { dateStyle: r } : r);
19
- }
20
- function $(t, r = g) {
21
- return f(t, typeof r == "string" ? { timeStyle: r } : r);
8
+ if ((!n || n === e.language) && c(`Unknown language code "${e.language}"`), !e.region) return;
9
+ let o;
10
+ try {
11
+ o = new Intl.DisplayNames("en-US", { type: "region" }).of(e.region);
12
+ } catch {
22
13
  }
23
- const s = {
14
+ (!o || o === "Unknown Region" || o === e.region) && c(`Unknown region code "${e.region}"`);
15
+ }
16
+ function D(e) {
17
+ const n = typeof e.defaultLanguage == "string" ? new Intl.Locale(e.defaultLanguage) : e.defaultLanguage, o = n.region ? `${n.language}-${n.region}` : n.language, a = e.translations ? structuredClone(e.translations) : {}, u = {
18
+ default: { dateStyle: "medium", timeStyle: "medium" },
19
+ short: { dateStyle: "short", timeStyle: "short" },
20
+ medium: { dateStyle: "medium", timeStyle: "medium" },
21
+ long: { dateStyle: "long", timeStyle: "long" },
22
+ full: { dateStyle: "full", timeStyle: "full" },
23
+ // date only formats
24
+ date: { dateStyle: "medium" },
25
+ shortDate: { dateStyle: "short" },
26
+ mediumDate: { dateStyle: "medium" },
27
+ longDate: { dateStyle: "long" },
28
+ fullDate: { dateStyle: "full" },
29
+ // time only formats
30
+ time: { timeStyle: "medium" },
31
+ shortTime: { timeStyle: "short" },
32
+ mediumTime: { timeStyle: "medium" },
33
+ longTime: { timeStyle: "long" },
34
+ fullTime: { timeStyle: "full" },
35
+ // overrides and custom formats
36
+ ...e.dateTimeFormats
37
+ }, s = {
38
+ // Expand all currency codes into number formats for currencies
39
+ ...Intl.supportedValuesOf("currency").reduce((t, r) => (t[r] = { style: "currency", currency: r }, t), {}),
40
+ // Add the default number format
41
+ default: {},
42
+ // Overrides and custom formats
43
+ ...e.numberFormats
44
+ }, i = h(new Intl.Locale(o));
45
+ w(i, v, { immediate: !0 });
46
+ const f = b(() => {
47
+ const { language: t, region: r } = i.value, g = [t];
48
+ return r && g.unshift(`${t}-${r}`), t !== o && g.push(o), g;
49
+ }), l = {
24
50
  get locale() {
25
- return l.value;
51
+ return i.value;
26
52
  },
27
53
  set locale(t) {
28
- l.value = t;
54
+ i.value = t;
29
55
  },
30
56
  get language() {
31
- return s.locale.language;
57
+ return l.locale.language;
32
58
  },
33
59
  set language(t) {
34
- s.locale = new Intl.Locale(t, { ...l.value });
60
+ l.locale = new Intl.Locale(t, { ...i.value });
35
61
  },
36
62
  get region() {
37
- return s.locale.region;
63
+ return l.locale.region;
38
64
  },
39
65
  set region(t) {
40
- s.locale = new Intl.Locale(s.language, { ...s.locale, region: t || void 0 });
66
+ l.locale = new Intl.Locale(l.language, { ...l.locale, region: t || void 0 });
41
67
  },
42
- n(t, r) {
68
+ n(t, r = "default") {
43
69
  if (t == null) return "";
44
- if (typeof r == "string") {
45
- const c = r;
46
- return new Intl.NumberFormat(s.locale, { style: "currency", currency: c }).format(t);
47
- } else {
48
- const c = r || d;
49
- return new Intl.NumberFormat(s.locale, c).format(t);
50
- }
70
+ const g = typeof r == "string" ? s[r] : r;
71
+ return g || c(`NumberFormat alias "${r}" not found`), new Intl.NumberFormat(l.locale, g).format(t);
51
72
  },
52
73
  t(t, r) {
53
- return s.tc(t, 1, r);
74
+ return l.tc(t, 1, r);
54
75
  },
55
- tc(t, r, c) {
56
- const y = j(o, t, m.value), v = new Intl.NumberFormat(s.locale, d);
57
- return N(y, Object.assign({ n: r }, c), v);
76
+ tc(t, r, g) {
77
+ const m = I(a, t, f.value), p = new Intl.NumberFormat(l.locale, s.default);
78
+ return k(m, Object.assign({ n: r }, g), p);
58
79
  },
59
- d: Object.assign(f, { date: p, time: $ })
80
+ d(t, r = "default") {
81
+ if (t == null || t === "") return "";
82
+ const g = t instanceof Date ? t : new Date(t), m = typeof r == "string" ? u[r] : r;
83
+ return m || c(`DateTimeFormat alias "${r}" not found`), new Intl.DateTimeFormat(i.value, m).format(g);
84
+ }
60
85
  };
61
- return F(s);
86
+ return $(l);
62
87
  }
63
- const w = /* @__PURE__ */ new WeakMap();
64
- function j(e, n, a) {
88
+ const d = /* @__PURE__ */ new WeakMap();
89
+ function I(e, n, o) {
65
90
  if (!n) throw new Error("No translation key specified");
66
91
  if (typeof n == "string") {
67
- let o = w.get(e);
68
- o || w.set(e, o = {});
69
- let i = o[a[0]];
70
- i || (o[a[0]] = i = {});
71
- let g = i[n];
72
- if (!g) {
73
- let u = e[n];
74
- u || (h(`Translation key "${n}" not found`), u = { [a[a.length - 1]]: n }), g = b(u, a), i[n] = g;
92
+ let a = d.get(e);
93
+ a || d.set(e, a = {});
94
+ let u = a[o[0]];
95
+ u || (a[o[0]] = u = {});
96
+ let s = u[n];
97
+ if (!s) {
98
+ let i = e[n];
99
+ i || (c(`Translation key "${n}" not found`), i = { [o[o.length - 1]]: n }), s = y(i, o), u[n] = s;
75
100
  }
76
- return g;
101
+ return s;
77
102
  } else
78
- return b(n, a);
103
+ return y(n, o);
79
104
  }
80
- function b(e, n) {
81
- let a;
105
+ function y(e, n) {
106
+ let o;
82
107
  for (const l of n)
83
- if (a = e[l], a) break;
84
- if (!a) {
108
+ if (o = e[l], o) break;
109
+ if (!o) {
85
110
  const l = n[n.length - 1];
86
- return h(`Translation missing default language "${l}" in`, e), { zero: "", singular: "", plural: "" };
111
+ return c(`Translation missing default language "${l}" in`, e), { zero: "", singular: "", plural: "" };
87
112
  }
88
- let o;
89
- const i = a.split(new RegExp("(?<!\\\\)(?:\\\\\\\\)*\\|"));
90
- if (i.length === 1) {
91
- const [l] = i;
92
- o = { zero: l, singular: l, plural: l };
93
- } else if (i.length === 2) {
94
- const [l, m] = i;
95
- o = { zero: m, singular: l, plural: m };
113
+ let a;
114
+ const u = o.split(new RegExp("(?<!\\\\)(?:\\\\\\\\)*\\|"));
115
+ if (u.length === 1) {
116
+ const [l] = u;
117
+ a = { zero: l, singular: l, plural: l };
118
+ } else if (u.length === 2) {
119
+ const [l, t] = u;
120
+ a = { zero: t, singular: l, plural: t };
96
121
  } else {
97
- const [l, m, f] = i;
98
- o = { zero: l, singular: m, plural: f };
122
+ const [l, t, r] = u;
123
+ a = { zero: l, singular: t, plural: r };
99
124
  }
100
- const { zero: g, singular: u, plural: d } = o;
101
- return { zero: g.trim(), singular: u.trim(), plural: d.trim() };
125
+ const { zero: s, singular: i, plural: f } = a;
126
+ return { zero: s.trim(), singular: i.trim(), plural: f.trim() };
102
127
  }
103
- function N(e, n, a) {
104
- const o = typeof n.n == "string" ? Number(n.n) : n.n;
105
- let i = o === 0 ? e.zero : o === 1 ? e.singular : e.plural;
106
- for (const [g, u] of Object.entries(n)) {
107
- const d = typeof u == "number" ? a.format(u) : typeof u == "string" ? u : u ? String(u) : "", l = new RegExp(`(.|^)({\\s*${g}\\s*})`, "gi");
108
- i = i.replaceAll(l, (m, f, p) => f === "\\" ? p : f + d);
128
+ function k(e, n, o) {
129
+ const a = typeof n.n == "string" ? Number(n.n) : n.n;
130
+ let u = a === 0 ? e.zero : a === 1 ? e.singular : e.plural;
131
+ for (const [s, i] of Object.entries(n)) {
132
+ const f = typeof i == "number" ? o.format(i) : typeof i == "string" ? i : i ? String(i) : "", l = new RegExp(`(.|^)({\\s*${s}\\s*})`, "gi");
133
+ u = u.replaceAll(l, (t, r, g) => r === "\\" ? g : r + f);
109
134
  }
110
- return i.trim();
135
+ return u.trim();
111
136
  }
112
137
  const S = Symbol.for("@juit/vue-i18n/translator");
113
- function P(e, n) {
114
- const o = L(typeof n == "string" ? { defaultLanguage: n } : n);
115
- return e.config.globalProperties.$t = o.t, e.config.globalProperties.$tc = o.tc, e.config.globalProperties.$n = o.n, e.config.globalProperties.$d = o.d, e.provide(S, o), e;
138
+ function L(e, n) {
139
+ const a = D(typeof n == "string" ? { defaultLanguage: n } : n);
140
+ return e.config.globalProperties.$t = a.t, e.config.globalProperties.$tc = a.tc, e.config.globalProperties.$n = a.n, e.config.globalProperties.$d = a.d, e.provide(S, a), e;
116
141
  }
117
- function x() {
118
- const e = I(S);
142
+ function z() {
143
+ const e = T(S);
119
144
  if (!e) throw new Error("No translator found in the Vue app");
120
145
  return e;
121
146
  }
122
147
  export {
123
- P as i18n,
124
- x as useTranslator
148
+ L as i18n,
149
+ z as useTranslator
125
150
  };
126
151
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../lib/translator.ts","../lib/index.ts"],"sourcesContent":["import { computed, reactive, shallowRef, warn } from 'vue'\n\nimport type { DateTimeFormats, I18nOptions, ISOCountry, Translation, TranslationKey } from './index'\nimport type { ISOLanguage } from './iso-639'\n\n/* ===== TRANSLATOR INTERFACE =============================================== */\n\n/**\n * Parameters for the formatting of a translation.\n *\n * This type is used to pass parameters to the `t` and `tc` methods of the\n * translator, allowing for the interpolation of values into the translated\n * message.\n *\n * When the parameter value is a number, it will be formatted using the `n`\n * formatter before being interpolated into the message.\n */\nexport interface TranslationParams {\n [ key: string ]: string | number\n}\n\n/**\n * The date input type for date and time translation\n *\n * When the input is a non-empty `string`, or a `number`, it will be constructed\n * into a `Date` object before being formatted.\n *\n * When the input is `null`, `undefined`, or an empty string, formatted result\n * will be a simple empty string.\n */\nexport type DateInput = Date | string | number | null | undefined\n\n/**\n * The translator interface for the application.\n *\n * This interface provides methods to translate messages, format numbers, and\n * format dates and times.\n *\n * Configured instances can be accessed using the `useI18n()` composition\n * function, which will provide an instance of the translator.\n */\nexport interface Translator {\n /** The current ISO-639-1 language code used by this translator. */\n language: ISOLanguage\n /** The region (if any) used by thus translator to localize translations. */\n region: ISOCountry | undefined\n /** The `Locale` used by this translator (merges `language` and `region`) */\n locale: Intl.Locale\n\n /**\n * Return the (possibly parameterized) translation for the specified message\n * in the current language.\n *\n * Internally, this method uses the `tc(...)` function with `n=1`, in order to\n * avoid duplication of message keys\n */\n t(key: TranslationKey | Translation, params?: TranslationParams): string\n\n /**\n * Return the (possibly parameterized) translation for the specified message\n * in the current language, with pluralization.\n *\n * For pluralization, translation messages should be separated by the pipe\n * character, like in Vue I18N. Example:\n *\n * * `\" one apple | {n} apples \"` when _two_ translations are separated by a\n * pipe, the first will be used for singular, the second for zero or plural\n * * `\" no apples | one apple | {n} apples \"` when _three_ translations are\n * separated by a pipe, the first will be used for zero, the second for\n * singular, the second for zero or plural\n *\n * For convenience, the `{n}` message parameter will always be contextualized\n * with the number, unless overridden in the `params` themselves.\n */\n tc(key: TranslationKey | Translation, n: number, params?: TranslationParams): string\n\n /**\n * Format a number into a string according to the current language.\n *\n * If the `currency` parameter is set, the number will be formatted as a\n * currency value, using the specified currency symbol.\n */\n n(value?: number | bigint | null | undefined, currency?: string): string\n\n /**\n * Format a number into a string according to the current language.\n *\n * When `format` is provided, it will be used to configure the number format,\n * otherwise the default `format.numberFormat` plugin option will be used.\n */\n n(value?: number | bigint | null | undefined, format?: Intl.NumberFormatOptions): string\n\n d: {\n /**\n * Format date and time according to the current language.\n *\n * When `style` is provided, it will be used to configure the date and time\n * format, otherwise the default `format.dateTimeFormat` plugin option will\n * be used.\n */\n (date?: DateInput, style?: DateTimeFormats['dateTimeFormat']): string\n /**\n * Format the date part (without time) according to the current language.\n *\n * When `style` is provided, it will be used to configure the date and time\n * format, otherwise the default `format.dateOnlyFormat` plugin option will\n * be used.\n */\n date(date?: DateInput, style?: DateTimeFormats['dateOnlyFormat']): string\n /**\n * Format the time part (without date) according to the current language.\n *\n * When `style` is provided, it will be used to configure the date and time\n * format, otherwise the default `format.timeOnlyFormat` plugin option will\n * be used.\n */\n time(date?: DateInput, style?: DateTimeFormats['timeOnlyFormat']): string\n }\n}\n\n/* ===== TRANSLATOR IMPLEMENTATION ========================================== */\n\n/** Create a _reactive_ translator object from the given options */\nexport function makeTranslator(options: I18nOptions): Translator {\n // Default locale, parsing the default language\n const defaultLocale: Intl.Locale = typeof options.defaultLanguage === 'string' ?\n new Intl.Locale(options.defaultLanguage) :\n options.defaultLanguage\n\n // Normalized default language (language-REGION)\n const defaultLanguage = defaultLocale.region ?\n `${defaultLocale.language}-${defaultLocale.region}` :\n defaultLocale.language\n\n const translations: InternalTranslations = options.translations ? structuredClone(options.translations) : {}\n const {\n dateOnlyFormat = { dateStyle: 'medium' },\n timeOnlyFormat = { timeStyle: 'medium' },\n dateTimeFormat = { dateStyle: 'medium', timeStyle: 'medium' },\n numberFormat = {},\n } = options.formats || {}\n\n // Current locale, from the browser's language settings\n const locale = shallowRef(new Intl.Locale(defaultLanguage))\n\n // Language order, from the current locale\n const languages = computed(() => {\n const { language, region } = locale.value\n const order: string[] = [ language ]\n if (region) order.unshift(`${language}-${region}`)\n if (language !== defaultLanguage) order.push(defaultLanguage)\n return order as any as LanguageKeys\n })\n\n // Date-time translator, from the current locale\n function dateTime(input?: DateInput, style = dateTimeFormat): string {\n if ((input == null) || (input === '')) return ''\n\n const date = input instanceof Date ? input : new Date(input)\n const options = typeof style === 'string' ? { dateStyle: style, timeStyle: style } : style\n return new Intl.DateTimeFormat(locale.value, options).format(date)\n }\n\n // Date-only translator, from the current locale\n function dateOnly(input?: DateInput, style = dateOnlyFormat): string {\n const options = typeof style === 'string' ? { dateStyle: style } : style\n return dateTime(input, options)\n }\n\n // Time-only translator, from the current locale\n function timeOnly(input?: DateInput, style = timeOnlyFormat): string {\n const options = typeof style === 'string' ? { timeStyle: style } : style\n return dateTime(input, options)\n }\n\n // The translator object (non-reactive)\n const translator = {\n get locale() {\n return locale.value\n },\n\n set locale(value: Intl.Locale) {\n locale.value = value\n },\n\n get language(): ISOLanguage {\n return translator.locale.language as ISOLanguage\n },\n\n set language(value: ISOLanguage) {\n translator.locale = new Intl.Locale(value, { ...locale.value })\n },\n\n get region(): ISOCountry | undefined {\n return translator.locale.region as ISOCountry\n },\n\n set region(value: ISOCountry | undefined) {\n translator.locale = new Intl.Locale(translator.language, { ...translator.locale, region: value || undefined })\n },\n\n n(value?: number | bigint | null | undefined, currencyOrOptions?: string | Intl.NumberFormatOptions): string {\n if (value == null) return '' // null or undefined produces an empty string\n if (typeof currencyOrOptions === 'string') {\n const currency = currencyOrOptions\n return new Intl.NumberFormat(translator.locale, { style: 'currency', currency }).format(value)\n } else {\n const options = currencyOrOptions || numberFormat\n return new Intl.NumberFormat(translator.locale, options).format(value)\n }\n },\n\n t(translation: TranslationKey | Translation, params?: TranslationParams): string {\n return translator.tc(translation, 1, params)\n },\n\n tc(translation: TranslationKey | Translation, n: number, params?: TranslationParams): string {\n const template = getTemplate(translations, translation, languages.value)\n const format = new Intl.NumberFormat(translator.locale, numberFormat)\n return replaceParams(template, Object.assign({ n }, params), format)\n },\n\n d: Object.assign(dateTime, { date: dateOnly, time: timeOnly }),\n } as const satisfies Translator\n\n // Return a reactive version of the translator\n return reactive(translator)\n}\n\n/* ===== TRANSLATION UTILITIES ============================================== */\n\ntype LanguageKeys = readonly [ string, ...string[] ]\ntype InternalTranslation = Record<string, string | undefined>\ntype InternalTranslations = Record<string, InternalTranslation>\ntype TranslationTemplate = { zero: string, singular: string, plural: string }\n\n/**\n * Cached parsed translation templates.\n *\n * The keys are:\n * 1) the translations instance (WeakMap key)\n * 2) the current language (first entry in the order)\n * 3) the translation key\n */\nconst caches = new WeakMap<InternalTranslations, Record<string, Record<string, TranslationTemplate>>>()\n\n/** Get the `TranslationTemplate` for the translation or translation key. */\nfunction getTemplate(\n translations: InternalTranslations,\n translation: TranslationKey | Translation,\n languages: LanguageKeys,\n): TranslationTemplate {\n if (! translation) throw new Error('No translation key specified')\n\n if (typeof translation === 'string') {\n // Get the cache for the messages instance\n let cache = caches.get(translations)\n if (! cache) caches.set(translations, cache = {})\n\n // Get the cache for the current language\n let languageCache = cache[languages[0]]\n if (! languageCache) cache[languages[0]] = languageCache = {}\n\n // Get the translation from the cache or parse it\n let template = languageCache[translation]\n if (! template) {\n let object = translations[translation]\n if (! object) {\n warn(`Translation key \"${translation}\" not found`)\n object = { [languages[languages.length - 1]!]: translation }\n }\n template = extractTemplate(object, languages)\n languageCache[translation] = template\n }\n\n return template\n } else {\n return extractTemplate(translation, languages)\n }\n}\n\n/**\n * Extract a message from a translation, according to its locale, and split\n * it into its parsed components: zero, singular, and plural.\n */\nfunction extractTemplate(\n translation: InternalTranslation,\n languages: LanguageKeys,\n): TranslationTemplate {\n let string: string | undefined = undefined\n\n for (const language of languages) {\n string = translation[language as any]\n if (string) break\n }\n\n if (! string) {\n const language = languages[languages.length - 1]\n warn(`Translation missing default language \"${language}\" in`, translation)\n return { zero: '', singular: '', plural: '' }\n }\n\n let parsed: TranslationTemplate\n\n const translations = string.split(/(?<!\\\\)(?:\\\\\\\\)*\\|/)\n if (translations.length === 1) {\n const [ singular ] = translations\n parsed = { zero: singular!, singular: singular!, plural: singular! }\n } else if (translations.length === 2) {\n const [ singular, plural ] = translations\n parsed ={ zero: plural!, singular: singular!, plural: plural! }\n } else {\n const [ zero, singular, plural ] = translations\n parsed ={ zero: zero!, singular: singular!, plural: plural! }\n }\n\n const { zero, singular, plural } = parsed\n return { zero: zero.trim(), singular: singular.trim(), plural: plural.trim() }\n}\n\n/** Replace the parameters in a translation template, returning a string */\nfunction replaceParams(\n template: TranslationTemplate,\n params: TranslationParams,\n format: Intl.NumberFormat,\n): string {\n // Select the template to use based on the \"n\" (number) parameter\n const n = typeof params.n === 'string' ? Number(params.n) : params.n\n let formatted = n === 0 ? template.zero :\n n === 1 ? template.singular :\n template.plural\n\n // Replace any property `{ prop }` with the associated value\n for (const [ prop, value ] of Object.entries(params)) {\n const string =\n typeof value === 'number' ? format.format(value) :\n typeof value === 'string' ? value :\n value ? String(value) : ''\n\n // Expression matches `{ xxx }` where `{` is _not_ preceded by a '\\'\n const expr = new RegExp(`(.|^)({\\\\s*${prop}\\\\s*})`, 'gi')\n formatted = formatted.replaceAll(expr, (_, before, token) => {\n return before === '\\\\' ? token : before + string\n })\n }\n\n // All done!\n return formatted.trim()\n}\n","import { inject } from 'vue'\n\nimport { makeTranslator } from './translator'\n\nimport type { App } from 'vue'\nimport type { ISOLanguage } from './iso-639'\nimport type { Translator } from './translator'\n\n/* ===== REFERENCE LANGUAGES AND COUNTRIES ================================== */\n\nexport type * from './iso-3166'\nexport type * from './iso-639'\n\n/* ===== TYPES FOR DECLARATION MERGING ====================================== */\n\n/**\n * I18n Configuration interface (to be merged with the actual configuration).\n *\n * This interface (intentionally empty) is used to merge the actual per-app\n * configuration of the translation system, in order to provide the correct\n * types to the rest of the system.\n *\n * Two properties are expected to be defined in the configuration:\n *\n * * `languages`: the list of supported languages for the application. Those\n * are ISO 639-1 language codes, and when specified, _every_\n * translation _must_ include a translation for each.\n * * `translationKeys`: the list of translation keys known by the application.\n * Those are the arbitrary keys used to identify the\n * messages to be translated with the `t` and `tc`\n * methods of `Translator`.\n *\n * To configure the types, follow the example below:\n *\n * ```ts\n * const translations = {\n * 'hello': { en: 'Hello, world!', de: 'Hallo, Welt!' }\n * } as const satisfies TranslationsOptions\n *\n * declare module '@juit/vue-i18n' {\n * export interface I18nConfiguration {\n * translationKeys: keyof typeof translations\n * languages: 'de' | 'en'\n * }\n * }\n * ```\n */\nexport interface I18nConfiguration {\n // languages: 'de' | 'en'\n // translationKeys: 'hello'\n}\n\n/* ===== FROM CONFIG TO TRANSLATIONS ======================================== */\n\n/** Extract the value associated with key `K` from type `T` if it extends `R`, otherwise return `R` */\ntype ExtractConfig<T, R, K extends string> = T extends { [ X in K ]: infer V } ? V extends R ? V : R : R\n\n/** The languages configured in `I18nConfiguration` or all ISO languages */\nexport type Language = ExtractConfig<I18nConfiguration, ISOLanguage, 'languages'>\n\n/** Base translations, either required when languages are set or all optional */\ntype BaseTranslation = ISOLanguage extends Language ? {\n readonly [ key in ISOLanguage ]?: string\n} : {\n readonly [ key in Language ]: string\n}\n\n/** Extended translations, supporting multiple region of each language */\ntype ExtendedTranslation = {\n readonly [ key in `${Language}-${string}` ]?: string\n}\n\n/** Prettify our `Translations` exported type */\ntype PrettifyTranslation<T> = { [ l in keyof T ]: T[l] }\n\n/**\n * A type describing the translations for a given translation key.\n *\n * When the `I18nConfig` interface is properly merged with and its contains\n * the `languages` property, this type will represent the list of required\n * translation keys (languages) required for each translation.\n *\n * When left unconfigured, all ISO languages will be considered as optional.\n */\nexport type Translation = PrettifyTranslation<BaseTranslation & ExtendedTranslation>\n\n/**\n * All known translation keys.\n *\n * When the `I18nConfig` interface is properly merged with and its contains\n * the `translationKeys` property, this type will represent the list of\n * translations keys available to the `t(...)` and `tc(...)` methods.\n *\n * When left unconfigured, this type will be `string`.\n */\nexport type TranslationKey = ExtractConfig<I18nConfiguration, string, 'translationKeys'>\n\n/* ===== MODULE INITIALIZATION ============================================== */\n\n/* Export the translator types */\nexport type * from './translator'\n\n/**\n * Options to initialize the translations handled by the translation system.\n *\n * Shared translations are defined as a key-value pair, where the key is the\n * identifier of the translation, and the value is an object containing the\n * translations for each language.\n */\nexport interface Translations {\n readonly [ key: string ]: Translation\n}\n\n/**\n * Options to initialize the date and time formats used by the translation\n * system.\n *\n * * `dateTimeFormat`: the default format for times and dates used by `$d(...)`.\n * * `dateOnlyFormat`: the default format for dates used by `$d.date(...)`.\n * * `timeOnlyFormat`: the default format for times used by `$d.time(...)`.\n * * `numberFormat`: the default format for numbers used by `$n(...)`.\n */\nexport interface DateTimeFormats {\n dateOnlyFormat?: Intl.DateTimeFormatOptions | Intl.DateTimeFormatOptions['dateStyle']\n timeOnlyFormat?: Intl.DateTimeFormatOptions | Intl.DateTimeFormatOptions['timeStyle']\n dateTimeFormat?: Intl.DateTimeFormatOptions | (Intl.DateTimeFormatOptions['dateStyle'] & Intl.DateTimeFormatOptions['timeStyle'])\n numberFormat?: Intl.NumberFormatOptions\n}\n\n/** The language or locale to use at construction */\nexport type DefaultLanguage = ISOLanguage | `${ISOLanguage}-${string}` | Intl.Locale\n\n/** Options to initialize the I18n plugin */\nexport interface I18nOptions {\n defaultLanguage: DefaultLanguage,\n translations?: Translations,\n formats?: DateTimeFormats,\n}\n\n/* ===== PUBLIC METHODS ===================================================== */\n\n/** Symbol for Vue injections */\nconst injectionSymbol = Symbol.for('@juit/vue-i18n/translator')\n\n/** Initialize the translation system plugin */\nexport function i18n(app: App, optionsOrLanguage: Language | I18nOptions): App {\n const options = typeof optionsOrLanguage === 'string' ?\n { defaultLanguage: optionsOrLanguage } : optionsOrLanguage\n\n const translator = makeTranslator(options)\n\n app.config.globalProperties.$t = translator.t\n app.config.globalProperties.$tc = translator.tc\n app.config.globalProperties.$n = translator.n\n app.config.globalProperties.$d = translator.d\n\n app.provide(injectionSymbol, translator)\n return app\n}\n\n/** Retrieve the translator instance from the Vue app */\nexport function useTranslator(): Translator {\n const translator = inject(injectionSymbol)\n if (! translator) throw new Error('No translator found in the Vue app')\n return translator as Translator\n}\n\n/* ===== VUE EXTENSIONS ===================================================== */\n\n// Extension to the Vue component interface\ndeclare module 'vue' {\n interface ComponentCustomProperties {\n /** Translate a message according to the current language */\n $t: Translator['t']\n /**\n * Return the (possibly parameterized) translation for the specified message\n * in the current language, with pluralization.\n */\n $tc: Translator['tc']\n /** Format a number into a string according to the current language */\n $n: Translator['n']\n /**\n * Format date and time using the specified style (defaults to `medium`)\n * according to the current language\n */\n $d: Translator['d']\n }\n}\n"],"names":["makeTranslator","options","defaultLocale","defaultLanguage","translations","dateOnlyFormat","timeOnlyFormat","dateTimeFormat","numberFormat","locale","shallowRef","languages","computed","language","region","order","dateTime","input","style","date","dateOnly","timeOnly","translator","value","currencyOrOptions","currency","translation","params","n","template","getTemplate","format","replaceParams","reactive","caches","cache","languageCache","object","warn","extractTemplate","string","parsed","singular","plural","zero","formatted","prop","expr","_","before","token","injectionSymbol","i18n","app","optionsOrLanguage","useTranslator","inject"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../lib/translator.ts","../lib/index.ts"],"sourcesContent":["import { computed, reactive, shallowRef, warn, watch } from 'vue'\n\nimport type {\n DateTimeFormatAlias,\n DateTimeFormats,\n I18nOptions,\n ISOCountry,\n NumberFormatAlias,\n NumberFormats,\n Translation,\n TranslationKey,\n} from './index'\nimport type { ISOLanguage } from './iso-639'\n\n/* ===== TRANSLATOR INTERFACE =============================================== */\n\n/**\n * Parameters for the formatting of a translation.\n *\n * This type is used to pass parameters to the `t` and `tc` methods of the\n * translator, allowing for the interpolation of values into the translated\n * message.\n *\n * When the parameter value is a number, it will be formatted using the `n`\n * formatter before being interpolated into the message.\n */\nexport interface TranslationParams {\n [ key: string ]: string | number\n}\n\n/**\n * The date input type for date and time translation\n *\n * When the input is a non-empty `string`, or a `number`, it will be constructed\n * into a `Date` object before being formatted.\n *\n * When the input is `null`, `undefined`, or an empty string, formatted result\n * will be a simple empty string.\n */\nexport type DateInput = Date | string | number | null | undefined\n\n/**\n * The translator interface for the application.\n *\n * This interface provides methods to translate messages, format numbers, and\n * format dates and times.\n *\n * Configured instances can be accessed using the `useI18n()` composition\n * function, which will provide an instance of the translator.\n */\nexport interface Translator {\n /** The current ISO-639-1 language code used by this translator. */\n language: ISOLanguage\n /** The region (if any) used by thus translator to localize translations. */\n region: ISOCountry | undefined\n /** The `Locale` used by this translator (merges `language` and `region`) */\n locale: Intl.Locale\n\n /**\n * Return the (possibly parameterized) translation for the specified message\n * in the current language.\n *\n * Internally, this method uses the `tc(...)` function with `n=1`, in order to\n * avoid duplication of message keys\n */\n t(key: TranslationKey | Translation, params?: TranslationParams): string\n\n /**\n * Return the (possibly parameterized) translation for the specified message\n * in the current language, with pluralization.\n *\n * For pluralization, translation messages should be separated by the pipe\n * character, like in Vue I18N. Example:\n *\n * * `\" one apple | {n} apples \"` when _two_ translations are separated by a\n * pipe, the first will be used for singular, the second for zero or plural\n * * `\" no apples | one apple | {n} apples \"` when _three_ translations are\n * separated by a pipe, the first will be used for zero, the second for\n * singular, the second for zero or plural\n *\n * For convenience, the `{n}` message parameter will always be contextualized\n * with the number, unless overridden in the `params` themselves.\n */\n tc(key: TranslationKey | Translation, n: number, params?: TranslationParams): string\n\n /**\n * Format a number according to the current language.\n *\n * When `format` is provided, it will be used to configure the number format.\n * This can be one of the aliases specified at initialization, or a\n * fully-fledged `Intl.NumberFormatOptions` object.\n */\n n(value?: number | bigint | null | undefined, format?: NumberFormatAlias | Intl.NumberFormatOptions): string\n\n /**\n * Format date and time according to the current language.\n *\n * When `format` is provided, it will be used to configure the date and time\n * format. This can be one of the aliases specified at initialization, or a\n * fully-fledged `Intl.DateTimeFormatOptions` object.\n */\n d(date?: DateInput, format?: DateTimeFormatAlias | Intl.DateTimeFormatOptions): string\n}\n\n/* ===== TRANSLATOR IMPLEMENTATION ========================================== */\n\nfunction checkLocale(locale: Intl.Locale): void {\n let languageString: string | undefined\n try {\n languageString = new Intl.DisplayNames('en-US', { type: 'language' }).of(locale.language)\n /* v8 ignore next */\n } catch { /* */ }\n\n if ((! languageString) || (languageString === locale.language)) {\n warn(`Unknown language code \"${locale.language}\"`)\n }\n\n if (! locale.region) return\n\n let regionString: string | undefined\n try {\n regionString = new Intl.DisplayNames('en-US', { type: 'region' }).of(locale.region)\n /* v8 ignore next */\n } catch { /* */ }\n\n if ((! regionString) || (regionString === 'Unknown Region') || (regionString === locale.region)) {\n warn(`Unknown region code \"${locale.region}\"`)\n }\n}\n\n/** Create a _reactive_ translator object from the given options */\nexport function makeTranslator(options: I18nOptions): Translator {\n // Default locale, parsing the default language\n const defaultLocale: Intl.Locale = typeof options.defaultLanguage === 'string' ?\n new Intl.Locale(options.defaultLanguage) :\n options.defaultLanguage\n\n // Normalized default language (language-REGION)\n const defaultLanguage = defaultLocale.region ?\n `${defaultLocale.language}-${defaultLocale.region}` :\n defaultLocale.language\n\n const translations: InternalTranslations = options.translations ? structuredClone(options.translations) : {}\n const dateTimeFormats: DateTimeFormats = {\n default: { dateStyle: 'medium', timeStyle: 'medium' },\n short: { dateStyle: 'short', timeStyle: 'short' },\n medium: { dateStyle: 'medium', timeStyle: 'medium' },\n long: { dateStyle: 'long', timeStyle: 'long' },\n full: { dateStyle: 'full', timeStyle: 'full' },\n\n // date only formats\n date: { dateStyle: 'medium' },\n shortDate: { dateStyle: 'short' },\n mediumDate: { dateStyle: 'medium' },\n longDate: { dateStyle: 'long' },\n fullDate: { dateStyle: 'full' },\n\n // time only formats\n time: { timeStyle: 'medium' },\n shortTime: { timeStyle: 'short' },\n mediumTime: { timeStyle: 'medium' },\n longTime: { timeStyle: 'long' },\n fullTime: { timeStyle: 'full' },\n\n // overrides and custom formats\n ...options.dateTimeFormats,\n }\n\n const numberFormats: NumberFormats = {\n // Expand all currency codes into number formats for currencies\n ...Intl.supportedValuesOf('currency').reduce((formats, currency) => {\n formats[currency] = { style: 'currency', currency }\n return formats\n }, {} as Record<string, Intl.NumberFormatOptions>),\n // Add the default number format\n default: {},\n // Overrides and custom formats\n ...options.numberFormats,\n }\n\n // Current locale, from the browser's language settings\n const locale = shallowRef(new Intl.Locale(defaultLanguage))\n watch(locale, checkLocale, { immediate: true })\n // checkLocale(locale.value)\n\n // Language order, from the current locale\n const languages = computed(() => {\n const { language, region } = locale.value\n const order: string[] = [ language ]\n if (region) order.unshift(`${language}-${region}`)\n if (language !== defaultLanguage) order.push(defaultLanguage)\n return order as any as LanguageKeys\n })\n\n // The translator object (non-reactive)\n const translator = {\n get locale() {\n return locale.value\n },\n\n set locale(value: Intl.Locale) {\n locale.value = value\n },\n\n get language(): ISOLanguage {\n return translator.locale.language as ISOLanguage\n },\n\n set language(value: ISOLanguage) {\n translator.locale = new Intl.Locale(value, { ...locale.value })\n },\n\n get region(): ISOCountry | undefined {\n return translator.locale.region as ISOCountry\n },\n\n set region(value: ISOCountry | undefined) {\n translator.locale = new Intl.Locale(translator.language, { ...translator.locale, region: value || undefined })\n },\n\n n(value?: number | bigint | null | undefined, format: string | Intl.NumberFormatOptions = 'default'): string {\n if (value == null) return '' // null or undefined produces an empty string\n\n const options = typeof format === 'string' ? numberFormats[format] : format\n if (! options) warn(`NumberFormat alias \"${format}\" not found`)\n\n return new Intl.NumberFormat(translator.locale, options).format(value)\n },\n\n t(translation: TranslationKey | Translation, params?: TranslationParams): string {\n return translator.tc(translation, 1, params)\n },\n\n tc(translation: TranslationKey | Translation, n: number, params?: TranslationParams): string {\n const template = getTemplate(translations, translation, languages.value)\n const format = new Intl.NumberFormat(translator.locale, numberFormats['default'])\n return replaceParams(template, Object.assign({ n }, params), format)\n },\n\n d(input?: DateInput, format: DateTimeFormatAlias | Intl.DateTimeFormatOptions = 'default'): string {\n if ((input == null) || (input === '')) return ''\n\n const date = input instanceof Date ? input : new Date(input)\n const options = typeof format === 'string' ? dateTimeFormats[format] : format\n if (! options) warn(`DateTimeFormat alias \"${format}\" not found`)\n\n return new Intl.DateTimeFormat(locale.value, options).format(date)\n },\n } as const satisfies Translator\n\n // Return a reactive version of the translator\n return reactive(translator)\n}\n\n/* ===== TRANSLATION UTILITIES ============================================== */\n\ntype LanguageKeys = readonly [ string, ...string[] ]\ntype InternalTranslation = Record<string, string | undefined>\ntype InternalTranslations = Record<string, InternalTranslation>\ntype TranslationTemplate = { zero: string, singular: string, plural: string }\n\n/**\n * Cached parsed translation templates.\n *\n * The keys are:\n * 1) the translations instance (WeakMap key)\n * 2) the current language (first entry in the order)\n * 3) the translation key\n */\nconst caches = new WeakMap<InternalTranslations, Record<string, Record<string, TranslationTemplate>>>()\n\n/** Get the `TranslationTemplate` for the translation or translation key. */\nfunction getTemplate(\n translations: InternalTranslations,\n translation: TranslationKey | Translation,\n languages: LanguageKeys,\n): TranslationTemplate {\n if (! translation) throw new Error('No translation key specified')\n\n if (typeof translation === 'string') {\n // Get the cache for the messages instance\n let cache = caches.get(translations)\n if (! cache) caches.set(translations, cache = {})\n\n // Get the cache for the current language\n let languageCache = cache[languages[0]]\n if (! languageCache) cache[languages[0]] = languageCache = {}\n\n // Get the translation from the cache or parse it\n let template = languageCache[translation]\n if (! template) {\n let object = translations[translation]\n if (! object) {\n warn(`Translation key \"${translation}\" not found`)\n object = { [languages[languages.length - 1]!]: translation }\n }\n template = extractTemplate(object, languages)\n languageCache[translation] = template\n }\n\n return template\n } else {\n return extractTemplate(translation, languages)\n }\n}\n\n/**\n * Extract a message from a translation, according to its locale, and split\n * it into its parsed components: zero, singular, and plural.\n */\nfunction extractTemplate(\n translation: InternalTranslation,\n languages: LanguageKeys,\n): TranslationTemplate {\n let string: string | undefined = undefined\n\n for (const language of languages) {\n string = translation[language as any]\n if (string) break\n }\n\n if (! string) {\n const language = languages[languages.length - 1]\n warn(`Translation missing default language \"${language}\" in`, translation)\n return { zero: '', singular: '', plural: '' }\n }\n\n let parsed: TranslationTemplate\n\n const translations = string.split(/(?<!\\\\)(?:\\\\\\\\)*\\|/)\n if (translations.length === 1) {\n const [ singular ] = translations\n parsed = { zero: singular!, singular: singular!, plural: singular! }\n } else if (translations.length === 2) {\n const [ singular, plural ] = translations\n parsed ={ zero: plural!, singular: singular!, plural: plural! }\n } else {\n const [ zero, singular, plural ] = translations\n parsed ={ zero: zero!, singular: singular!, plural: plural! }\n }\n\n const { zero, singular, plural } = parsed\n return { zero: zero.trim(), singular: singular.trim(), plural: plural.trim() }\n}\n\n/** Replace the parameters in a translation template, returning a string */\nfunction replaceParams(\n template: TranslationTemplate,\n params: TranslationParams,\n format: Intl.NumberFormat,\n): string {\n // Select the template to use based on the \"n\" (number) parameter\n const n = typeof params.n === 'string' ? Number(params.n) : params.n\n let formatted = n === 0 ? template.zero :\n n === 1 ? template.singular :\n template.plural\n\n // Replace any property `{ prop }` with the associated value\n for (const [ prop, value ] of Object.entries(params)) {\n const string =\n typeof value === 'number' ? format.format(value) :\n typeof value === 'string' ? value :\n value ? String(value) : ''\n\n // Expression matches `{ xxx }` where `{` is _not_ preceded by a '\\'\n const expr = new RegExp(`(.|^)({\\\\s*${prop}\\\\s*})`, 'gi')\n formatted = formatted.replaceAll(expr, (_, before, token) => {\n return before === '\\\\' ? token : before + string\n })\n }\n\n // All done!\n return formatted.trim()\n}\n","import { inject } from 'vue'\n\nimport { makeTranslator } from './translator'\n\nimport type { App } from 'vue'\nimport type { ISOCurrency } from './iso-4217'\nimport type { ISOLanguage } from './iso-639'\nimport type { Translator } from './translator'\n\n/* ===== REFERENCE LANGUAGES AND COUNTRIES ================================== */\n\nexport type * from './iso-3166'\nexport type * from './iso-639'\n\n/* ===== TYPES FOR DECLARATION MERGING ====================================== */\n\n/**\n * I18n Configuration interface (to be merged with the actual configuration).\n *\n * This interface (intentionally empty) is used to merge the actual per-app\n * configuration of the translation system, in order to provide the correct\n * types to the rest of the system.\n *\n * Two properties are expected to be defined in the configuration:\n *\n * * `languages`: the list of supported languages for the application. Those\n * are ISO 639-1 language codes, and when specified, _every_\n * translation _must_ include a translation for each.\n * * `translationKeys`: the list of translation keys known by the application.\n * Those are the arbitrary keys used to identify the\n * messages to be translated with the `t` and `tc`\n * methods of `Translator`.\n * * `dateTimeFormats`: the date and time formats _aliases_ used by the\n * application.\n * * `numberFormats`: the number formats _aliases_ used by the application.\n *\n * To configure the types, follow the example below:\n *\n * ```ts\n * const translations = {\n * 'hello': { en: 'Hello, world!', de: 'Hallo, Welt!' }\n * } as const satisfies Translations\n *\n * const dateTimeFormats = {\n * // override the default format\n * default: { dateStyle: 'short', timeStyle: 'short' },\n * // add a new custom format\n * custom: {\n * day: '2-digit',\n * month: '2-digit',\n * year: 'numeric',\n * weekday: 'short',\n * timeZone: 'UTC',\n * },\n * } as const satisfies DateTimeFormats\n *\n * const numberFormats = {\n * speed: { style: 'unit', unit: 'kilometer-per-hour' },\n * } as const satisfies NumberFormats\n *\n * declare module '@juit/vue-i18n' {\n * export interface I18nConfiguration {\n * languages: 'de' | 'en',\n * translationKeys: keyof typeof translations,\n * dateTimeFormats: keyof typeof dateTimeFormats,\n * numberFormats: keyof typeof numberFormats,\n * }\n * }\n * ```\n */\nexport interface I18nConfiguration {\n // intentionally empty\n}\n\n/* ===== FROM CONFIG TO TRANSLATIONS ======================================== */\n\n/** Extract the value associated with key `K` from type `T` if it extends `R`, otherwise return `R` */\ntype ExtractConfig<T, R, K extends string> = T extends { [ X in K ]: infer V } ? V extends R ? V : R : R\n\n/** The languages configured in `I18nConfiguration` or all ISO languages */\nexport type Language = ExtractConfig<I18nConfiguration, ISOLanguage, 'languages'>\n\n/** Base translations, either required when languages are set or all optional */\ntype BaseTranslation = ISOLanguage extends Language ? {\n readonly [ key in ISOLanguage ]?: string\n} : {\n readonly [ key in Language ]: string\n}\n\n/** Extended translations, supporting multiple region of each language */\ntype ExtendedTranslation = {\n readonly [ key in `${Language}-${string}` ]?: string\n}\n\n/** Prettify our `Translations` exported type */\ntype PrettifyTranslation<T> = { [ l in keyof T ]: T[l] }\n\n/**\n * A type describing the translations for a given translation key.\n *\n * When the `I18nConfig` interface is properly merged with and its contains\n * the `languages` property, this type will represent the list of required\n * translation keys (languages) required for each translation.\n *\n * When left unconfigured, all ISO languages will be considered as optional.\n */\nexport type Translation = PrettifyTranslation<BaseTranslation & ExtendedTranslation>\n\n/**\n * All known translation keys.\n *\n * When the `I18nConfig` interface is properly merged with and its contains\n * the `translationKeys` property, this type will represent the list of\n * translations keys available to the `t(...)` and `tc(...)` methods.\n *\n * When left unconfigured, this type will be `string`.\n */\nexport type TranslationKey = ExtractConfig<I18nConfiguration, string, 'translationKeys'>\n\n/**\n * All known date and time formats aliases.\n *\n * When the `I18nConfig` interface is properly merged with and its contains\n * the `dateTimeFormats` property, this type will represent the list of\n * date and time formats available to the `d(...)` method.\n *\n * When left unconfigured, this type will be `string`.\n */\nexport type DateTimeFormatAlias = ExtractConfig<I18nConfiguration, string, 'dateTimeFormats'>\n | 'default' | 'short' | 'medium' | 'long' | 'full'\n | 'date' | 'shortDate' | 'mediumDate' | 'longDate' | 'fullDate'\n | 'time' | 'shortTime' | 'mediumTime' | 'longTime' | 'fullTime'\n\n/**\n * All known number formats aliases.\n *\n * When the `I18nConfig` interface is properly merged with and its contains\n * the `numberFormats` property, this type will represent the list of\n * date and time formats available to the `n(...)` method.\n *\n * When left unconfigured, this type will be `string`.\n */\nexport type NumberFormatAlias = ExtractConfig<I18nConfiguration, string, 'numberFormats'>\n | 'default' | ISOCurrency\n\n/* ===== MODULE INITIALIZATION ============================================== */\n\n/* Export the translator types */\nexport type * from './translator'\n\n/**\n * Options to initialize the translations handled by the translation system.\n *\n * Shared translations are defined as a key-value pair, where the key is the\n * identifier of the translation, and the value is an object containing the\n * translations for each language.\n */\nexport interface Translations {\n readonly [ key: string ]: Translation\n}\n\n/**\n * Options to initialize the date and time format _aliases_ used by the\n * translation system.\n *\n * The default aliases (each can be overridden) are:\n *\n * ```ts\n * {\n * default: { dateStyle: 'medium', timeStyle: 'medium' },\n * short: { dateStyle: 'short', timeStyle: 'short' },\n * medium: { dateStyle: 'medium', timeStyle: 'medium' },\n * long: { dateStyle: 'long', timeStyle: 'long' },\n * full: { dateStyle: 'full', timeStyle: 'full' },\n *\n * // date only formats\n * date: { dateStyle: 'medium' },\n * shortDate: { dateStyle: 'short' },\n * mediumDate: { dateStyle: 'medium' },\n * longDate: { dateStyle: 'long' },\n * fullDate: { dateStyle: 'full' },\n *\n * // time only formats\n * time: { timeStyle: 'medium' },\n * shortTime: { timeStyle: 'short' },\n * mediumTime: { timeStyle: 'medium' },\n * longTime: { timeStyle: 'long' },\n * fullTime: { timeStyle: 'full' },\n * }\n * ```\n */\nexport interface DateTimeFormats {\n readonly [ key: string ]: Intl.DateTimeFormatOptions\n}\n\n/**\n * Options to initialize the number format _aliases_ used by the translation\n * system.\n *\n * The default aliases (each can be overridden) are:\n *\n * ```ts\n * {\n * default: { }, // use the default number format\n * EUR: { style: 'currency', currency: 'EUR' },\n * USD: { style: 'currency', currency: 'USD' },\n * // ... all currency codes can be used as aliases\n * }\n * ```\n */\nexport interface NumberFormats {\n readonly [ key: string ]: Intl.NumberFormatOptions\n}\n\n/** The language or locale to use at construction */\nexport type DefaultLanguage = ISOLanguage | `${ISOLanguage}-${string}` | Intl.Locale\n\n/** Options to initialize the I18n plugin */\nexport interface I18nOptions {\n defaultLanguage: DefaultLanguage,\n translations?: Translations,\n dateTimeFormats?: DateTimeFormats,\n numberFormats?: NumberFormats,\n}\n\n/* ===== PUBLIC METHODS ===================================================== */\n\n/** Symbol for Vue injections */\nconst injectionSymbol = Symbol.for('@juit/vue-i18n/translator')\n\n/** Initialize the translation system plugin */\nexport function i18n(app: App, optionsOrLanguage: Language | I18nOptions): App {\n const options = typeof optionsOrLanguage === 'string' ?\n { defaultLanguage: optionsOrLanguage } : optionsOrLanguage\n\n const translator = makeTranslator(options)\n\n app.config.globalProperties.$t = translator.t\n app.config.globalProperties.$tc = translator.tc\n app.config.globalProperties.$n = translator.n\n app.config.globalProperties.$d = translator.d\n\n app.provide(injectionSymbol, translator)\n return app\n}\n\n/** Retrieve the translator instance from the Vue app */\nexport function useTranslator(): Translator {\n const translator = inject(injectionSymbol)\n if (! translator) throw new Error('No translator found in the Vue app')\n return translator as Translator\n}\n\n/* ===== VUE EXTENSIONS ===================================================== */\n\n// Extension to the Vue component interface\ndeclare module 'vue' {\n interface ComponentCustomProperties {\n /** Translate a message according to the current language */\n $t: Translator['t']\n /**\n * Return the (possibly parameterized) translation for the specified message\n * in the current language, with pluralization.\n */\n $tc: Translator['tc']\n /** Format a number into a string according to the current language */\n $n: Translator['n']\n /**\n * Format date and time using the specified style (defaults to `medium`)\n * according to the current language\n */\n $d: Translator['d']\n }\n}\n"],"names":["checkLocale","locale","languageString","warn","regionString","makeTranslator","options","defaultLocale","defaultLanguage","translations","dateTimeFormats","numberFormats","formats","currency","shallowRef","watch","languages","computed","language","region","order","translator","value","format","translation","params","n","template","getTemplate","replaceParams","input","date","reactive","caches","cache","languageCache","object","extractTemplate","string","parsed","singular","plural","zero","formatted","prop","expr","_","before","token","injectionSymbol","i18n","app","optionsOrLanguage","useTranslator","inject"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juit/vue-i18n",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -18,7 +18,7 @@
18
18
  "vue": "^3.5.13"
19
19
  },
20
20
  "devDependencies": {
21
- "@plugjs/eslint-plugin": "^0.3.11",
21
+ "@plugjs/eslint-plugin": "^0.3.12",
22
22
  "@types/node": "<21",
23
23
  "@vitejs/plugin-vue": "^5.2.1",
24
24
  "@vitest/coverage-v8": "^3.0.5",